为什么TypeScript的Record<number, string>允许返回字符串键却直接赋值报错
Record类型赋值差异的原因及合法key说明
两段代码行为不同的核心原因
这个差异来自TypeScript两套不同的类型检查规则:
- 第二段代码是对象字面量直接赋值给显式声明类型的变量,此时会触发严格的「对象字面量多余属性检查」:要求字面量的所有key必须和目标类型的索引类型完全匹配。你声明的目标类型是
Record<number, string>,要求key为数字类型,但字面量里的key是字符串foo,类型不匹配所以直接报错。 - 第一段代码里的
obj是没有显式声明类型的变量,TypeScript会自动推导它的类型为{ foo: string },之后将obj作为返回值赋值时,触发的是普通类型兼容性检查,不会触发多余属性检查。
而JavaScript运行时本身会把所有对象的数字key自动转为字符串(比如obj[123]等价于obj["123"]),TypeScript针对索引签名类型的兼容性做了放宽:只要现有对象的属性值符合目标类型的值类型要求,就允许赋值给Record<number, string>类型,所以第一段代码不会报错。
如果给第一段的obj也显式声明类型const obj: Record<number, string> = { foo: 'bar' },就会和第二段代码一样触发报错。
Record类型的合法key类型
Record<K, T>的第一个泛型参数K必须满足extends string | number | symbol的约束,仅支持三类取值:
- 任意字符串/数字/符号基础类型:比如
Record<string, any>表示所有key为字符串的对象,Record<number, string>表示所有key为数字的对象 - 字面量联合类型:比如
Record<'name' | 'age', string>表示仅包含name、age两个字符串key的对象,Record<1 | 2 | 3, boolean>表示仅包含1、2、3三个数字key的对象 - 模板字面量类型:比如
Record<prefix-${string}, string>表示所有key以prefix-开头的字符串的对象
内容的提问来源于stack exchange,提问作者Shulz
相关产品推荐
相关产品推荐

