创建新对象时keyof T类型信息丢失的原因及疑问
TypeScript 索引类型赋值错误解析:为什么
{ [key]: "ho" }不匹配预期类型? 问题场景
我在封装mongoose查询的抽象函数时遇到类型问题,简化后的场景如下:
function x<T extends object, K extends keyof T> ( obj: T, key: K ) { // Type '{ [x: string]: string; }' is not assignable to // type '{ [Q in K]: string; }'.(2322) const y: {[P in K]: {[Q in P]: string}}[K] = { [key]: "ho" }; }
明明已明确满足类型约束,为何会出现此类型错误?TypeScript为何将key的类型判定为string,它也可能是number啊?
错误原因解析
1. 动态属性名的类型推断规则
当使用{ [key]: "ho" }这种动态属性名创建对象时,TypeScript会默认将索引类型推断为string。这是因为JavaScript的底层机制:无论传入的key是number还是symbol类型,最终都会被转换为字符串(比如{ [123]: "test" }实际等价于{ "123": "test" })。因此TypeScript在这里会把对象类型推断为{ [x: string]: string },而非你期望的精确属性类型。
2. 精确类型与索引签名类型的兼容性问题
你定义的预期类型{[P in K]: {[Q in P]: string}}[K]展开后等价于{ [Q in K]: string },这是一个仅包含K类型属性的精确对象类型。但推断出的{ [x: string]: string }是字符串索引签名类型,允许任意字符串属性。在TypeScript的类型系统中,精确类型无法兼容更宽泛的索引签名类型——因为后者可能包含超出K范围的额外属性,违反了类型约束。
解决方案
方案1:类型断言直接指定类型
通过类型断言告诉TypeScript,当前对象就是你期望的精确类型:
function x<T extends object, K extends keyof T> ( obj: T, key: K ) { const y: {[P in K]: {[Q in P]: string}}[K] = { [key]: "ho" } as {[Q in K]: string}; }
方案2:使用Object.fromEntries生成精确类型
Object.fromEntries可以根据键值对数组生成对象,配合类型断言能更精准地约束类型:
function x<T extends object, K extends keyof T> ( obj: T, key: K ) { const y: {[P in K]: {[Q in P]: string}}[K] = Object.fromEntries([[key, "ho"]]) as {[Q in K]: string}; }
方案3:封装工具函数辅助类型推断
创建一个泛型工具函数,确保返回的是包含指定属性的精确对象类型:
function createExactProp<K extends PropertyKey>(key: K, value: string): { [Q in K]: string } { return { [key]: value } as { [Q in K]: string }; } function x<T extends object, K extends keyof T> ( obj: T, key: K ) { const y: {[P in K]: {[Q in P]: string}}[K] = createExactProp(key, "ho"); }
内容的提问来源于stack exchange,提问作者NoBullsh1t
相关产品推荐
相关产品推荐

