0%

typescript-index-signature

为什么我的对象无法添加新属性?

今天在网上看到一个问题,在typescript中定义了一个对象,却无法添加新属性,代码如下:

1
2
const obj = {};
obj.prop = 'value'; // Error: TS2339: Property prop does not exist on type {}

乍一看到这个问题,我有点疑惑,为什么要这样写呢?我以往的习惯是,需要使用什么类型,都会提前定义好,然后使用就行了,这个类型包括哪些属性,都是提前设置好的。但是这段代码里面的obj是一个空对象,什么属性都没有,感觉实际应用中这种情况很少。

但是作为一个典型的例子研究一下,还是不错的。这个错误产生的原因是作者对于typescript中的索引签名(index signature)不熟悉导致的。我们来分析一下原因。

索引签名

typescript在解析obj的时候,并不知道它里面会有哪些属性,属性的key是什么类型?属性的值又是什么类型?完全不知道,所以当用户试图添加一个新属性的时候,typescript就会报错。
解决的办法也很简单,我们需要提前告知typescript这些关于属性的信息。这个信息就是索引签名

1
2
3
4
5
6
interface looseObject {
[key: string]: any; // 添加索引签名:属性名是字符串类型,属性值是任意类型
}

const obj: looseObject = {};
obj.prop = 'value'; // OK

使用Record类型

这个例子更简单的办法是使用Record类型,Record类型在定义特定类型的对象时特别有用。Record<string, any>表示一个对象类型,其属性名是字符串类型,属性值可以是任意类型。

1
2
const obj: Record<string, any> = {};
obj.prop = 'value'; // OK

索引签名的使用场景

那么什么场景下需要使用索引签名呢?一般来说,当属性的个数不固定的时候,就需要使用索引签名了。

统计学生成绩

比如要统计学生成绩,每个科目对应一个分数,不同专业的学生科目是不一样的,这时候就可以使用索引签名来定义一个对象类型。

1
2
3
4
5
6
7
8
9
10
interface StudentScores {
[subject: string]: number; // 科目名是字符串类型,分数是数字类型
}

const scores: StudentScores = {
math: 90,
english: 85,
physics: 92,
};
scores.chemistry = 88; // 可以添加新的科目

CSS-In-JS的使用

在CSS-In-JS的场景中,通常需要动态添加样式属性,这时候索引签名也非常有用。下面的代码定义了一个主题(Theme),其中包括colorspacing属性,对于color属性,我们不可能列出所有可能的颜色名称,这时候可以使用索引签名就非常有用了。spacing也是同样的道理。

1
2
3
4
5
6
7
8
type Theme = {
colors: {
[colorName: string]: string;
};
spacing: {
[size: string]: number;
};
};

今天就到这里了,祝大家编程愉快,喜欢就点个关注,我要去打弹弓了,明天见!