如何用类泛型约束构造函数参数类型?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 } } }
遇到的错误
- 映射表方式错误:
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' - 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
相关产品推荐
相关产品推荐

