为什么在Ts中Object.keys总是返回字符串数组?
一个常见的问题
在很多情况下,我们需要遍历一个对象的所有Key,就像这样
1 | let marks = { |
Ts会立即报错,因为Object.keys的类型是这样
1 | interface ObjectConstructor { |
这种时候我们往往需要对key进行断言
1 | let marks = { |
这段代码只是简单的对类型的断言,更准确的断言实际还应该排除symbol
类型,因为对象的键也可能是symbol,但是Object.keys
只会遍历对象的可枚举的字符串属性。
同时这里也将数字转换为字符串,因为对象的键除了symbol
,都会被转换为字符串。
为什么Ts没有处理?
在GitHub上事实上有很多讨论,其中一个Pull request的讨论下有详细的回答。
Ts作者说明了使用key of T
仅仅只在类型系统中工作,在运行时,类型会被抹除掉,而在实际场景,往往对象会拥有更多的key,这时类型是不安全的。
特别的,在fon in
循环中,如果被推断的对象是一个泛型参数,Ts中将其key推断为 Extract<keyof T, string>
,而在其它情况下,总是会被推断为string
1 | const testCases = <T>(object: T)=> { |
这里其实还是不太明白为什么使用泛型参数可以,而使用其他类型不行,可能与泛型参数是根据输入的类型自动推断有关。
其他的相关函数
其他的对象遍历函数基本与Object.keys
表现一致,只是可以通过泛型传递值的类型。
1 | interface ObjectConstructor { |
小结
这点实际上是由于Ts类型仅存在编译时,和运行时的类型是无关的,所以在这种依赖于运行时的函数时,无法保证类型安全,因此给了一个最宽泛的string
并让开发者自己断言。
不过在问题的讨论下,也有举例Readonly
的评论,因为Readonly
实际也仅存在编译类型检查时,运行时也仍然是可写的。