如何为含动态生成键值的对象定义TypeScript类型?
问题
我有一个预定义的字符串数组STRINGS_ARR,想要用其中的字符串创建对象STRINGS_OBJ。对象的每个键需通过移除输入字符串的下划线动态生成,值则为输入字符串本身(例如输入'Some_Test_Name',键为'SomeTestName',值为'Some_Test_Name')。我已实现生成该对象的代码,但无法正确定义对象结构,导致TypeScript无法根据键正确推断值的类型。目前我的类型定义中Value类型为never,核心代码如下:
type InputStringMap = { [Key in KeyName<InputString>]: Extract<InputString, Key> }; type Key = keyof InputStringMap; type Value = InputStringMap[Key]; // ^? type Value = never
完整代码示例:
type InputString = 'Test_String_1' | 'Test_String_2'; const STRINGS_ARR: InputString[] = ['Test_String_1', 'Test_String_2']; type KeyName<S extends string> = string extends S ? 'Error' : S extends `${infer A}_${infer B}` ? KeyName<`${A}${B}`> : S; type InputStringMap = { [Key in KeyName<InputString>]: Extract<InputString, Key> }; type Key = keyof InputStringMap; type Value = InputStringMap[Key]; // ^? type Value = never const keyValuePairs = STRINGS_ARR.map<[Key, Value]>((str) => [str.replace(/_/g, '') as Key, str as Value]); const STRINGS_OBJ = Object.fromEntries(keyValuePairs) as InputStringMap;
解决方案
问题出在InputStringMap的定义逻辑上:当前是遍历KeyName<InputString>(所有处理后的驼峰键),再用Extract<InputString, Key>匹配原字符串,但Key是驼峰格式,和原字符串的下划线格式完全不重叠,所以Extract返回never,最终导致Value类型异常。
正确的做法是遍历原字符串联合类型的每个成员,为每个成员生成对应的驼峰键,并将值设为该成员本身。修改InputStringMap的定义即可解决问题:
type InputStringMap = { [S in InputString as KeyName<S>]: S };
这个类型利用了TypeScript的映射类型重命名键(as子句)特性:对每个原字符串S,将它的键重命名为KeyName<S>(处理后的驼峰名),值直接设为S本身。这样每个键和对应值的类型就完全对应了。
修改后的完整代码:
type InputString = 'Test_String_1' | 'Test_String_2'; const STRINGS_ARR: InputString[] = ['Test_String_1', 'Test_String_2']; type KeyName<S extends string> = string extends S ? 'Error' : S extends `${infer A}_${infer B}` ? KeyName<`${A}${B}`> : S; // 修正后的类型定义 type InputStringMap = { [S in InputString as KeyName<S>]: S }; type Key = keyof InputStringMap; type Value = InputStringMap[Key]; // ^? type Value = "Test_String_1" | "Test_String_2" const keyValuePairs = STRINGS_ARR.map<[Key, Value]>((str) => [ str.replace(/_/g, '') as Key, str as Value ]); const STRINGS_OBJ = Object.fromEntries(keyValuePairs) as InputStringMap; // 测试:TypeScript能正确推断键对应的值类型 STRINGS_OBJ.TestString1; // 类型为 "Test_String_1" STRINGS_OBJ.TestString2; // 类型为 "Test_String_2"
补充说明
- 映射类型的
as子句是TypeScript 4.1+引入的特性,用于在映射时重命名对象的键,完美适配这种“基于原值生成键”的场景。 - 修正后
Value类型会正确推断为原字符串的联合类型,STRINGS_OBJ的每个键也会对应到正确的原字符串类型,解决了类型推断失效的问题。
内容的提问来源于stack exchange,提问作者Mandeep Singh Bhatia
相关产品推荐
相关产品推荐

