如何限制泛型参数实现单属性对象动态创建工厂函数?
问题描述
我想编写一个工厂函数来创建对象实例,生成的对象仅包含一个属性,但属性名称和对应值的类型需要动态指定(属性名用常量指定即可)。目标是创建属性为单元素数组的对象,我尝试使用泛型与类型约束,但未完全掌握其用法。
原代码如下:
// 目标:创建一个属性为单元素数组的对象 function factory<TContainer, TElement>(key: string, element: TElement): TContainer { return {[key]: [element]}; }
期望的使用方式:
// 期望用法 // 给定一个类型 type T1 = { key1: number[]; }; // 替代这种写法 const e1a: T1 = { key1: [1]}; const e1b: T1 = { key1: [1, 2]}; const e1c: T1 = { key1: []}; // 我想这么写 const e1z: T1 = factory<T1, number>('key1', 999); // e1z === {key1: [999]}; const e2z: T2 = factory<T2, string>('key2', 'z'); // e2z === {key2: ['z']}; // 另外也想不用显式类型的写法 const onthefly:{onthefly:string[]} = factory<{onthefly:string[]}, string>('onthefly', 'onthefly'); // onthefly === {onthefly: ['onthefly']}; // 更理想的是编译器能自动推断类型,不用我指定 const magic = factory('magic', 3); // magic:{magic:number[]} === {magic:[3]}
但运行时出现错误:
Type '{ [x: string]: TElement[]; }' is not assignable to type 'TContainer'. 'TContainer' could be instantiated with an arbitrary type which could be unrelated to '{ [x: string]: TElement[]; }'.
解决方案
问题核心在于原泛型的约束过于松散,TContainer可以是任意类型,TypeScript无法确认返回的对象与TContainer结构匹配。我们需要调整泛型参数,让类型之间建立明确的关联。
方案1:自动推断属性名与元素类型
使用两个泛型参数:K约束为字符串字面量类型(代表属性名),T代表元素类型。返回值类型明确为仅包含该属性的对象,属性值为单元素数组:
function factory<K extends string, T>(key: K, element: T): { [P in K]: T[] } { return { [key]: [element] } as { [P in K]: T[] }; }
验证期望用法:
- 针对预定义类型:
type T1 = { key1: number[] }; const e1z: T1 = factory('key1', 999); // 类型匹配,无错误 type T2 = { key2: string[] }; const e2z: T2 = factory('key2', 'z'); // 正常工作
- 即时定义类型:
const onthefly: { onthefly: string[] } = factory('onthefly', 'onthefly'); // 符合预期
- 自动推断类型:
const magic = factory('magic', 3); // 自动推断出 magic 的类型是 { magic: number[] }
方案2:支持显式指定目标容器类型
如果你需要显式指定目标容器类型,可以调整泛型,让TContainer约束为包含单个属性的对象,同时关联属性名和元素类型:
function factory<TContainer extends Record<K, T[]>, K extends string, T>(key: K, element: T): TContainer { return { [key]: [element] } as unknown as TContainer; }
用法示例:
type T1 = { key1: number[] }; // 显式指定所有泛型参数 const e1z = factory<T1, 'key1', number>('key1', 999); // 利用类型推断,仅指定TContainer const e1z2 = factory<T1>(('key1' as const), 999);
原代码报错原因
原代码中TContainer没有任何约束,TypeScript无法保证返回的{ [key]: [element] }与TContainer结构一致。比如若有人调用factory<number, string>('key', 'val'),返回的对象显然不是number类型,因此编译器会报错。通过给泛型添加约束,让类型之间建立关联,即可解决该问题。
内容的提问来源于stack exchange,提问作者Vito De Tullio
相关产品推荐
相关产品推荐

