问题
平时在写代码时,经常会遇到遍历一个对象的所有属性,我们可以使用Object.keys
来拿到对象所有的key
,然后在通过myObject[key]
来获取属性的值。但是如果在TS中直接这样写的话,就会有类型错误。
const myObject = {
a: 1,
b: 2,
c: 3
}
Object.keys(myObject).forEach(key => {
// Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
console.log(myObject[key])
})
那么一般我们遇到这种问题该怎么解决呢?
解答
一般遇到这种问题,最直接的做法就是就是把类型校验错误的地方改正,由于Object.keys
返回的类型是string[]
,所以我们代码中的key
的类型是string
,但是myObject
的key
类型却是"a"|"b"|"c"
。所以我们只要把key
的类型重新as
一下就行了:
const myObject = {
a: 1,
b: 2,
c: 3
}
Object.keys(myObject).forEach(key => {
console.log(myObject[key as keyof typeof MyObject])
})
但是这样会有不好的地方就是,我们所有用Object.keys
的地方都需要手动这样as
一下很麻烦,而且我们应该少用as
来进行强制类型断言。
所以我们可以自己声明一个方法,对这个方法设置返回的类型:
const myObject = {
a: 1,
b: 2,
c: 3
}
const keys = Object.keys as <T>(o: T) => (keyof T)[];
keys(myObject).forEach(key => {
console.log(myObject[key])
})
这样的话就不报类型不匹配的错误了。 还有另一种方式可以直接修改内置的Object.keys
的方法的返回类型:
interface ObjectConstructor {
keys<T>(o: T): ReadonlyArray<keyof T>;
}
const myObject = {
a: 1,
b: 2,
c: 3
};
Object.keys(myObject).forEach(key => {
console.log(myObject[key]);
})
但是这种做法一般是不推荐的,因为内置的实现中Object.keys
返回string[]
是有一定的道理的,如果我们强制修改了它的返回值类型,在一些场景中就会有问题。 比如:
const keys = Object.keys as <T>(o: T) => (keyof T)[];
keys(MyObject).forEach(key => {
console.log(MyObject[key])
})
const a = {x: 1, y: 2}
const b: {x: number} = a
const k1 = keys(b) //"x"[]
const k2 = Object.keys(b) //string[]
这个时候 k1 的类型是有问题的。