前言
在使用了 typescript 的项目开发中,有时候为了某个对象进行声明,经常需要写完 interface 之后,在赋值的时候又写了一遍相似的代码;当想对它增加一个键值,又得去增加 interface 的字段声明。因此很想和声明一个变量那样,在写键值的时候直接进行类型声明。
对象的传统声明方式
对于对象的类型声明,我们经常使用 interface、type、class 的方式,先对整体类型进行声明,然后再去赋值,例如:
typescript">interface IInterface {
id: number;
name: string;
}
type IType = {
id: number,
name: string,
}
class IClass {
id: number;
name: string;
}
const data: IType = {
id: 1,
name: 'A'
}
但是这样会出现代码重复,特别是当键值对很多或者不确定数量的时候,经常需要去维护类型声明和赋值两部分“长得很像”的代码。
使用类型断言声明
有时候我们只是希望在后续的赋值操作中不会出现类型错误,或者只是一次性的声明不打算复用。那么可以采用下面的方式实现:
typescript">const data = {
id: 1 as number,
name: 'A' as string,
}
data.id = '123'; // 不允许
data.name = 'B'; // 允许
实际上,这里使用到了TypeScript 的类型断言,这里的 as 是 Assertion 的意思,将对 as 后面的类型(或者字面量表示的类型)进行推断。
它的原理大概是这样的:
- data 没有声明类型,所以被当做 any
- 给 data 赋值了一个对象,每个字段的键值类型也都是 any
- 使用类型断言,例如 id: 1 as number,将 any 覆盖为 number.
存在的类型安全问题
使用类型断言无法表达 readonly 以及赋值范围,将会导致类型安全问题。参考下面例子:
typescript">interface IType {
readonly id: number;
sex: 0 | 1;
}
const data: IType = {
id: 1,
sex: 1
};
data.id = 2; // 不允许
data.sex = 2; // 不允许
typescript">const data = {
id: 1 as number, // 没有正确语法位置可以写 readonly
// 例如 id: 1 as readonly number 是错误语法
sex: 1 as 0 | 1 // 写了 0 | 1,实际上会被推断为 number
};
data.id = 2; // 允许⚠️
data.sex = 2; // 允许⚠️
虽然只用类型断言无法表达 readonly 和赋值范围,但我们可以曲线救国,糅合两种类型声明方式,但显得很杂。
以 vue data 为例
下面以 vue 的 data 为例,可以直观对比出代码量的缩减:
typescript">// 使用传统声明方式
interface IData {
id: number;
name: string;
age: number;
addr: string;
phone: string;
sex: 0 | 1;
height: number | string;
weight: number | string;
birthday: Date;
}
export default {
data(): IData {
return {
id: 1,
name: '',
age: 25,
addr: '',
phone: '',
sex: 0,
height: 180,
weight: '75kg',
birthday: new Date('1995-01-01')
}
}
}
typescript">// 使用类型断言
interface IDataMin {
readonly id: number;
sex: 0 | 1;
}
export default {
data(): IDataMin {
return {
id: 1, // 已在上面声明
name: '' as string,
age: 25 as number,
addr: '' as string,
phone: '' as string,
sex: 0, // 已在上面声明
height: 180 as number | string,
weight: '75kg' as number | string,
birthday: new Date('1995-01-01') as Date
}
}
}
不适用的情况
- 需要设置较多 readonly、赋值范围的时候
- 类型声明需要被复用的时候
- 项目风格要求类型与赋值解耦的时候