TypeScript高级泛型问题:关联数组配置与值对象的类型约束
TypeScript泛型实现:根据配置数组约束对象的键值类型
问题核心
你原代码的问题在于:T[number]['type']会取配置数组中所有type的联合类型,导致映射后每个key的类型都是Date | string | string[],而不是对应各自配置项的type。比如mydate本应只接受Date,但原代码会允许它传入字符串或数组。
解决方案一:用Extract提取对应key的类型
这种方式直接明了,不需要用到infer:
// 先定义类型标识到实际TS类型的映射表,方便扩展 type TypeMap = { date: Date; string: string; arrayOfString: string[]; }; type ItemConfig = { key: string; type: keyof TypeMap }; function test<T extends Readonly<ItemConfig[]>>( config: T, // 遍历配置数组的所有key,为每个key匹配对应类型 values: { [K in T[number]['key']]: // 提取配置数组中key等于K的元素,获取它的type,再映射到实际类型 TypeMap[Extract<T[number], { key: K }>['type']] } ) { return null; } const testConfig = [ { key: 'mydate', type: 'date' }, { key: 'mystring', type: 'string' }, { key: 'myArraystring', type: 'arrayOfString' } ] as const; // 合法:所有值类型匹配配置 const test1Values = { mydate: new Date(), mystring: 'nice', myArraystring: ['a', 'b', 'c'] }; test(testConfig, test1Values); // 非法:mydate传入字符串会触发类型错误 const test2Values = { mydate: '2024-01-01', // ❌ Type 'string' is not assignable to type 'Date' mystring: 'nice', myArraystring: ['a', 'b', 'c'] }; test(testConfig, test2Values);
解决方案二:用infer实现类型推断
如果你想理解infer的用法,下面是用infer解构数组元素类型的版本:
type TypeMap = { date: Date; string: string; arrayOfString: string[]; }; type ItemConfig = { key: string; type: keyof TypeMap }; // 用infer推断数组元素的key和type,转换成单个键值对类型 type ConfigToEntry<T> = T extends ReadonlyArray<infer U> ? U extends { key: infer K; type: infer TType } ? K extends string ? { [Key in K]: TypeMap[TType & keyof TypeMap] } : never : never : never; // 把多个键值对类型合并成一个完整的对象类型 type MergeEntries<T> = T extends { [k: string]: infer V } ? { [k in keyof T]: V } : never; function test<T extends Readonly<ItemConfig[]>>( config: T, values: MergeEntries<ConfigToEntry<T>> ) { return null; }
关于infer的核心解释
infer是TypeScript条件类型中的关键字,作用是在类型匹配过程中推断出未知的子类型,并赋值给一个变量供后续使用:
T extends ReadonlyArray<infer U>:如果T是只读数组,就把数组的元素类型推断为U;U extends { key: infer K; type: infer TType }:接着从元素类型U中,推断出key的类型为K,type的类型为TType;- 最后用推断出的
K和TType生成对应的键值对类型,再合并成完整对象。
简单来说,infer就是类型层面的“解构赋值”,用来从复杂类型(比如数组、函数、嵌套对象)中提取出我们需要的子类型。
内容的提问来源于stack exchange,提问作者Exayy
相关产品推荐
相关产品推荐

