You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript:如何使getCommandBuilder返回对应类型的命令构建器函数?

问题描述

我定义了名为ServiceType的枚举,每个枚举值对应一个服务,同时存在对应的配置接口和命令构建器函数——该函数接收配置接口实例并生成调用服务的命令对象。

我希望实现getCommandBuilder函数,接收枚举值后返回对应的命令构建器函数;但问题在于,TypeScript要求该构建器函数的参数必须实现所有配置接口,而非对应枚举值的专属配置接口。

示例代码如下:

const enum ServiceType {
    SERVICE_1,
    SERVICE_2,
}

interface Service1Config {
    field1: string;
}

interface Service2Config {
    field2: number;
}

const commandBuilder1 = (_: Service1Config) : void => {}

const commandBuilder2 = (_: Service2Config) : void => {}

const getCommandBuilder = (serviceType: ServiceType) => {
    switch (serviceType) {
        case ServiceType.SERVICE_1:
            return commandBuilder1
        case ServiceType.SERVICE_2:
            return commandBuilder2
    }
}

const myServiceType = ServiceType.SERVICE_1
const myServiceConfig = {field1: "Type one"} as Service1Config

const myCommandBuilder = getCommandBuilder(myServiceType)

myCommandBuilder(myServiceConfig)   // ERROR!

(实际代码中会遍历包含所有服务类型和配置的数组,无法直接硬编码指定myCommandBuilder = commandBuilder1)

TypeScript给出的错误信息如下:

example.ts:32:18 - error TS2345: Argument of type 'Service1Config' is not assignable to parameter of type 'Service1Config & Service2Config'.
  Property 'field2' is missing in type 'Service1Config' but required in type 'Service2Config'.

如何让TypeScript识别:调用getCommandBuilder(ServiceType.SERVICE_1)返回的函数仅需Service1Config类型参数,调用getCommandBuilder(ServiceType.SERVICE_2)返回的函数仅需Service2Config类型参数?


解决方案

核心思路是通过类型映射+泛型约束,让getCommandBuilder的返回值类型与传入的枚举值强关联,而非返回所有构建器类型的交集。

基础实现方式

  1. 先定义服务类型到配置类型的映射,明确每个枚举值对应的配置:
// 建立服务类型与配置类型的一一映射
type ServiceConfigMap = {
    [ServiceType.SERVICE_1]: Service1Config;
    [ServiceType.SERVICE_2]: Service2Config;
};
  1. 将getCommandBuilder改为泛型函数,让TypeScript根据传入的枚举值推断返回的构建器参数类型:
const getCommandBuilder = <T extends ServiceType>(serviceType: T): (config: ServiceConfigMap[T]) => void => {
    switch (serviceType) {
        case ServiceType.SERVICE_1:
            return commandBuilder1 as (config: ServiceConfigMap[T]) => void;
        case ServiceType.SERVICE_2:
            return commandBuilder2 as (config: ServiceConfigMap[T]) => void;
        default:
            throw new Error(`未知服务类型: ${serviceType}`);
    }
};

修改后,TypeScript会正确推断:

  • 传入ServiceType.SERVICE_1时,返回函数的参数类型为Service1Config
  • 传入ServiceType.SERVICE_2时,返回函数的参数类型为Service2Config

测试验证:

const myServiceType = ServiceType.SERVICE_1
const myServiceConfig = {field1: "Type one"} as Service1Config

const myCommandBuilder = getCommandBuilder(myServiceType)
myCommandBuilder(myServiceConfig) // 不再报错

// SERVICE_2场景同样能正确约束
const service2Config = {field2: 123} as Service2Config
const builder2 = getCommandBuilder(ServiceType.SERVICE_2)
builder2(service2Config) // 通过类型检查

批量服务场景优化

如果服务数量较多,可进一步将构建器也纳入类型映射,让代码更易维护,也适配遍历服务数组的场景:

// 配置类型映射
type ServiceConfigMap = {
    [ServiceType.SERVICE_1]: Service1Config;
    [ServiceType.SERVICE_2]: Service2Config;
};

// 自动推导构建器类型
type CommandBuilderMap = {
    [K in keyof ServiceConfigMap]: (config: ServiceConfigMap[K]) => void;
};

// 构建器实例集合
const commandBuilders: CommandBuilderMap = {
    [ServiceType.SERVICE_1]: commandBuilder1,
    [ServiceType.SERVICE_2]: commandBuilder2,
};

// 简化getCommandBuilder实现
const getCommandBuilder = <T extends ServiceType>(serviceType: T): CommandBuilderMap[T] => {
    return commandBuilders[serviceType];
};

新增服务时,只需扩展ServiceConfigMap和commandBuilders即可,无需修改getCommandBuilder的核心逻辑。


内容的提问来源于stack exchange,提问作者ArifulH8

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 06:05:21