TypeScript中的object出现的错误

object是个很基础也很常见的引用类型,比如我用typescript定义一个object类型的变量

1
let a: object;

乍一看没什么问题,但却_暗藏玄机_

比如我现在想定义一个函数,它能判断对象哪些属性是number类型,然后返回一个由这些属性组成的新的对象:

1
2
3
4
5
6
7
8
9
10
11
const isNumber = (object: object) => {
// 在一个函数里,改变传入的对象本身是不好的
const result = { ...object }
Object.keys(result).forEach(key => {
const value = result[key];
if(typeof value !== 'number') {
delete result[key];
}
})
return result;
}

这样看上去没什么问题,但是编译器会报错:

image-20210510173337280

上面提示不能用key作为{}类型的索引,但result为什么是{}类型呢?它为什么是个空对象呢?

这就是object类型的弊端,它覆盖的范围太广,几乎除了原始类型外的所有类型都是由它而衍生的,原型链的终点都有它

看下面一段代码就明白了:

1
2
3
4
let a: object;
a = {name: 'jack'}
a = () => {}
a = []

在这三种情况下,ts都没有报错

于是上面的问题就找到原因了,_js引擎_并不知道这个result具体是个字面量对象,还是函数或者说是索引类型,所以它非常人性化的给你返回了{},而key当作{}的索引就报错了。

解决办法

方案1

通过 keyof 的方式可以获取ts 类型的属性key的值

适用与非函数场景

1
2
3
4
5
6
var foo = {
a: '1',
b: '2'
}
// 这里typeof foo => foo的类型 等同于 interface Foo { a: string; b: string; }// typeof foo === Foo,这里只所以用 typeof foo,因为这样方便,对于不想写interface的直接量对象很容易获取它的类型// keyof typeof foo这里只获取 Foo的类型的key值,注意这个keyof后面一定是 typescript的类型
type FooType = keyof typeof foo; var getPropertyValue = Object.keys(foo).map(item => foo[item as FooType])

方案2

将参数类型改为_key-value_对象

1
2
3
4
5
6
7
8
9
10
11
12

const isNumber = (object: {[key: string]: unknown}) => {
// 在一个函数里,改变传入的对象本身是不好的
const result = { ...object }
Object.keys(result).forEach(key => {
const value = result[key];
if(typeof value !== 'number') {
delete result[key];
}
})
return result;
}