2024年5月9日发(作者:)
a record is preferred over an
index signature
在 TypeScript 中,有两种主要的方式用于描述对象
类型:属性和索引签名。属性是一个直接描述对象属性的
方式,它定义了对象的属性名和属性值类型。而索引签名
则是一种使用“键”来访问并描述对象属性的方式。在使
用 TypeScript 进行类型检查时,我们可以使用这两种方
式来描述对象类型。然而,在实际应用中,一个记录类型
(Record Type)往往比索引签名更为优秀。
何为记录类型?
记录类型是 TypeScript 中的一种类型定义方法,它
定义了一个记录类型,可以被用来将类似的可枚举属性聚
合成一个类型,并为属性提供具体的类型。例如:
``` type SomeType = Record
这种方式能够将一个对象的属性名和值的类型定义的
更加明确。这里的 `Record` 表示的是一种提供类型定义
的工具函数,其定义了一个由字符串类型作为键的对象,
并且每一个键所对应的值的类型都必须是数字类型。换句
话说,这个定义了一个拥有任意键但值全部为数字类型的
对象。
为什么记录类型比索引签名优秀?
索引签名比记录类型更加灵活,因为索引签名能够描
述任意类型的属性访问。这就使得索引签名在某些场合下
更加适用。但是,对于非常规的属性访问类型,会给
TypeScript 带来许多困惑和错误提示。
举个例子:
``` interface Foo { [prop: string]: string;
bar: number; }
let x: Foo = { bar: 1 }; let y: string = ;
```
上述代码中定义了一个接口 `Foo`,它包含了一个索
引签名和一个名为 `bar` 的属性,属性的类型分别为
`string` 和 `number`。接下来,我们定义了一个变量
`x`,类型为 `Foo`,它仅包含 `bar` 属性。最后,我们
定义了一个字符串类型变量 `y`,并令它等于变量 `x` 中
一个名为 `qux` 的属性值,这个属性实际上是不存在的。
在这个例子中,由于 `x` 中没有名为 `qux` 的属
性,因此 TypeScript 会提示以下错误:
``` Property 'qux' does not exist on type
'Foo'. Did you mean to access the 'bar' property
instead? ```
这种错误提示显然是非常有帮助的。然而,如果我们
修改接口 `Foo`,并将索引签名改为记录类型:
``` interface Foo { [prop: string]: string;
bar: number; }
let x: Record
= { bar: 1 }; let y: string = ; ```
这时,我们发现无论我们向变量 `y` 中访问哪个不存
在的属性名,TypeScript 都不会给出任何错误提示。这显
然是一个不好的设计,因为一些类型错误可能会不被及时
发现并解决。
因此,在大多数情况下,我们应当优先使用记录类
型,以更好地减少类型错误的发生。
疑问:那既然记录类型定义时限制的是字符串类型作
为键,我们在定义时就没有更好的表达自己的设计思路?
回答:在一些特定的场合下,我们可以向 `Record`
函数中传入其它类型参数以定制更加符合需要的记录类
型。例如:
``` type Resource = "users" | "roles" |
"permissions" type ACL = Record
```
在这个例子中,我们将字符串类型的联合类型作为
`Resource` 类型的定义。然后,我们就可以使用这个类型
来定义一个名为 `ACL` 的记录类型,并将其作为访问控制
列表的键值类型。在这里,我们利用了字符串类型的联合
类型,更好地表述了应用场景中的业务含义。
总结
记录类型是 TypeScript 中用来定义可枚举属性的一
种优秀方式。与索引签名不同的地方在于,记录类型定义
了一个具体的属性名,从而增强了代码的类型安全性。在
使用 TypeScript 进行开发时,我们应当优先使用记录类
型,以更好地减少类型错误的发生。
发布评论