为何TypeScript无法识别泛型的键为string类型?
问题解析:为什么
Record<string, any>约束下keyof T仍不是string类型? 先看示例代码:
function funcWithGeneric<T extends Record<string, any>>(propertyName: keyof T){ propertyName.toUpperCase() // ^ 报错: // Property 'toUpperCase' does not exist on type 'string | number | symbol'. // Property 'toUpperCase' does not exist on type 'number'.(2339) }
你原本以为Record<string, any>的约束能让TypeScript认定propertyName是string类型,但实际并非如此,原因如下:
Record<string, any>的定义是“键类型为string、值类型为any的对象”,但T extends Record<string, any>表示T可以是这个对象的超集——也就是说,T除了拥有string类型的键,还能额外包含number或symbol类型的键。- 举个实际例子,T可以是
{ username: string, [999]: boolean },这个类型完全符合extends Record<string, any>的约束,但此时keyof T的类型是"username" | 999,属于string | number | symbol的联合类型。 keyof操作符会返回对象所有可能键的类型,自然包含了T中额外的number/symbol键,所以TypeScript无法确定propertyName一定是string,也就不允许直接调用toUpperCase()。
如果要确保propertyName是string类型,可以修改泛型约束:
// 方式1:限制T不能有number/symbol类型的键 function funcWithGeneric<T extends Record<string, any> & { [key: number | symbol]: never }>(propertyName: keyof T){ propertyName.toUpperCase() // 不再报错 } // 方式2:显式定义键的类型范围 function funcWithGeneric<K extends string, T extends Record<K, any>>(propertyName: K){ propertyName.toUpperCase() // 不再报错 }
内容的提问来源于stack exchange,提问作者Gibb Sonn
相关产品推荐
相关产品推荐

