关于TypeScript官方手册keyof索引签名相关说明的疑问
TypeScript keyof 索引签名规则原理解答
1. 底层设计逻辑
TypeScript 对索引签名的 keyof 结果做了差异化处理,本质是在贴合JavaScript原生运行规则的同时,保证类型检查的合理性:
- JavaScript 运行时中所有对象的数字键都会被强制转为字符串,
obj[0]和obj["0"]完全等价 - TypeScript 的规则既要兼容JS的实际运行表现,也要保证类型约束的有效性,所以对两种索引签名做了不同的
keyof返回处理。
2. 为什么 keyof { [k: string]: boolean } 是 string | number
你声明的字符串索引签名,意味着所有字符串类型的键都是合法的。由于JS中数字键一定会被转成字符串,所以用数字作为键访问字符串索引的对象,运行时100%合法。TypeScript 为了兼容这种常见写法,特意把number加入了keyof的返回结果,允许你直接用数字索引这类对象,不需要手动转字符串。
这里的number不是说对象真的存在数字类型的键,只是TS提供的语法兼容,符合常规的编码习惯。
3. 为什么 keyof { [n: number]: unknown } 只有 number
如果声明的是数字索引签名,规则上只有能转为有效数字的键才是合法的,不是所有字符串都可以作为索引。比如你写obj["abc"]访问数字索引对象,运行时虽然也会转成字符串,但"abc"不是有效数字索引,类型层面是不允许的。
如果keyof返回string | number,就等于放开了所有字符串的索引权限,违背了数字索引的约束目的。所以TS直接限定keyof的结果只有number,保证你用非数字字符串索引的时候会触发类型报错,保障类型安全。
4. 你的测试代码为什么看起来和规则矛盾
你测试中写的a1["2"] = 6没有报错,是因为TypeScript会对字面量字符串做特殊识别:"2"属于可以合法转为数字的字符串字面量,TS会对这类值做放行,不会触发类型错误。如果你换成无法转成数字的字符串,就会立刻报错:
let a1: Arrayish = {}; a1["abc"] = 6; // 触发类型错误:类型“"abc"”不能作为索引类型“Arrayish”的索引
这个细粒度的类型校验逻辑,和前面的核心规则完全一致,不存在冲突。
内容的提问来源于stack exchange,提问作者Neutrino
相关产品推荐
相关产品推荐

