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

如何用类泛型约束构造函数参数类型?TypeScript技术求助

TypeScript 泛型工厂类:绑定键与对应值类型的问题

需求与现有代码

我需要将一个对象按其键映射为多种不同结构,每个键值对单独处理:先把对象转为条目数组,再映射为Policy对象并执行。期望用法如下:

type ValuesMap = { key1: Value1; key2: Value2; ... };
const valuesMap: ValuesMap = getValues();
const results = Object.entries(valuesMap).map(([key, value]) => policyFactory.createPolicy(key, value).apply());

为此我定义了抽象Policy类和工厂类:

type Key = keyof ValuesMap;
type Value<K extends Key> = ValuesMap[K];

abstract class Policy<K extends Key> {
  constructor(protected readonly value: Value<K>) {}
  abstract apply(): unknown; // 补充返回类型,避免隐式any
}

class ConcreteKey1Policy extends Policy<'key1'> {
  // 要求构造函数仅接受Value1类型,传入其他类型时TS报错
  apply() { /* 具体实现 */ }
}

// ... 其他ConcretePolicy类

class PolicyFactory {
  private readonly constructorMap: {
    [K in Key]: new (value: Value<K>) => Policy<K>;
  } = {
    key1: ConcreteKey1Policy,
    key2: ConcreteKey2Policy,
    // 对应ValuesMap的所有键
  };

  createPolicy<K extends Key>(type: K, value: Value<K>): Policy<K> {
    // 方式1:构造函数映射表,触发error1
    return new this.constructorMap[type](value);

    // 方式2:switch分支,触发error2
    switch(type) {
      case 'key1':
        return new ConcreteKey1Policy(value);
      // ... 其他case
    }
  }
}

遇到的错误

  1. 映射表方式错误:type "key1" is not assignable to type 'K'. "key1" is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'Key'
  2. switch分支方式错误:Argument of type 'Value1 | Value2 | Value3 | Value4' is not assignable to parameter of type 'Key1'. Type 'Value2' is not assignable to type 'Value1'

我理解错误原因,但不知道如何让工厂的泛型将type和value的类型严格绑定——当前用extends Key会约束为Key的任意子类型,但我需要约束为联合类型Key的单个成员。


解决方案

方案1:给工厂方法添加函数重载(最类型安全)

通过函数重载为每个Key单独定义签名,让TypeScript能精确推断参数类型,同时解决内部类型不匹配问题:

class PolicyFactory {
  private readonly constructorMap: {
    [K in Key]: new (value: Value<K>) => Policy<K>;
  } = {
    key1: ConcreteKey1Policy,
    key2: ConcreteKey2Policy,
    // ...
  };

  // 重载签名:每个Key对应独立的类型约束
  createPolicy(type: 'key1', value: Value1): Policy<'key1'>;
  createPolicy(type: 'key2', value: Value2): Policy<'key2'>;
  // ... 为ValuesMap的每个键添加对应重载

  // 实现签名:处理通用逻辑
  createPolicy<K extends Key>(type: K, value: Value<K>): Policy<K> {
    return new this.constructorMap[type](value);
  }
}

这样调用createPolicy时,传入的type和value会自动做类型校验,函数内部也不会再报错。

方案2:类型断言快速修复

如果不想写大量重载,可以用类型断言告诉TypeScript参数类型是匹配的:

映射表方式修复:

createPolicy<K extends Key>(type: K, value: Value<K>): Policy<K> {
  const Constructor = this.constructorMap[type] as new (value: Value<K>) => Policy<K>;
  return new Constructor(value);
}

switch分支方式修复:

createPolicy<K extends Key>(type: K, value: Value<K>): Policy<K> {
  switch(type) {
    case 'key1':
      return new ConcreteKey1Policy(value as Value1);
    case 'key2':
      return new ConcreteKey2Policy(value as Value2);
    // ... 其他case
    default:
      throw new Error(`Unsupported key: ${type}`);
  }
}

方案3:简化实现——直接用处理函数映射表

如果不需要单独的Policy类实例,也可以跳过工厂类,直接定义一个键到处理函数的映射表,更简洁:

// 定义处理函数映射表,用satisfies确保类型匹配ValuesMap
const policyHandlers = {
  key1: (value: Value1) => new ConcreteKey1Policy(value).apply(),
  key2: (value: Value2) => new ConcreteKey2Policy(value).apply(),
  // ... 对应所有Key
} satisfies { [K in Key]: (value: Value<K>) => ReturnType<Policy<K>['apply']> };

// 处理Object.entries的类型缺陷(默认返回[string, unknown][])
const results = (Object.entries(valuesMap) as Array<[Key, Value<Key>]>).map(([key, value]) => {
  return policyHandlers[key](value as Value<typeof key>);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 08:12:39