Typescript如何提取const对象的值类型作为新类型的键并实现类型安全函数
报错原因
报错本质是TypeScript类型系统的推导能力限制:
- 对于
KeysAsKeys<T>,它的键集合直接和T的键集合完全匹配,TS可以直接确认Partial<KeysAsKeys<T>>和KeysAsKeys<T>的类型是兼容的,因此强制断言可以通过。 - 但
ValuesAsKeys<T>是通过键重映射生成的新类型,TS没有办法自动建立「T的所有值的集合」和「ValuesAsKeys<T>的键集合」的等价关联:既无法确认循环遍历所有值后已经填满了所有需要的键,也无法处理多个键对应同一个值时的自动去重场景,因此直接将Partial<ValuesAsKeys<T>>断言为ValuesAsKeys<T>会被判定为类型不兼容。
解决方案
可以通过双重断言绕过TS的类型兼容性检查,业务逻辑本身可以保证最终返回的对象符合ValuesAsKeys<T>类型,不会有运行时问题。修改后的代码如下:
type BaseObject = Record<string, string>; type ValuesAsKeys<T extends BaseObject> = { [K in keyof T as T[K]]: number; }; function useValuesAsKeys<T extends BaseObject>(arg: T): ValuesAsKeys<T> { const x: Partial<ValuesAsKeys<T>> = {}; for (const value of Object.values(arg) as Array<T[keyof T]>) { x[value] = 1; } // 先转为unknown再断言为目标类型,绕过TS的限制 return x as unknown as ValuesAsKeys<T>; } // 测试用例 const obj = {key1: 'value1', key2: 'value2'} as const; const result = useValuesAsKeys(obj); // result的类型正确推导为 { value1: number; value2: number }
如果需要更强的类型安全,可以额外加泛型约束限制输入的T的所有值都是唯一字符串,日常业务场景下上述方案已经足够使用。
内容的提问来源于stack exchange,提问作者Eric Chen
相关产品推荐
相关产品推荐

