条款11: 额外属性检查的局限性
目录:
- 理解什么是额外类型检查;
- 理解为什么会存在额外类型检查、和常规的类型检查有什么区别;
- 掌握局限性表现在什么地方;
1. 额外类型检查
额外类型检查是指当将值或者变量引用赋值给另一个对象变量时,会触发类型检查器的一种检查方式。
常规的类型检查主要是判断两个变量类型是否是具有子集的关系,如果是具有子集的关系,则可以进行复制,见条款7。
额外类型属性检查的一种表现形式为,字面量对象赋值时的属性检查,有时也被称为严格的字面量检查,首先我们看一下下面的例子:
interface IA {
name: string;
age: number;
}
const a: IA = {
name: 'yunxi',
age: 18,
hobby: ['chouyan', 'hejiu', 'tangtou']
}
此时我们的代码会报错,
Object literal may only specify known properties, and 'hobby' does not exist in type 'IA' 对象文本只能指定已知属性,并且类型‘IA’中不存在‘hobby’。
他会告诉我们,我们想要将一个字面量对象赋值给类型IA时,会触发一个额外属性的检查,他会告诉我们我们想要赋值的对象中的属性是否满足要求。他仅仅是发生在字面量赋值的场景下,所以也称为“严格的字面量检查”;
当我们利用一个中间变量作为转换时,上述的问题就不复存在了:
interface IA {
name: string;
age: number;
}
const obj = {
name: 'yunxi',
age: 18,
hobby: ['chouyan', 'hejiu', 'tangtou']
}
const a: IA = obj;
因为严格的字面量检查只发生在字面量赋值的时候,const obj = {},这个赋值语句发生了字面量检查,但是将 obj 赋值给 a 时,没有额外类型检查,因为 obj 是一个内存地址的引用,而对于静态类型检查器而言,在代码未运行时,他并不知道变量地址的一个真实的类型,所以他只能通过上下文的推导,所以对于 obj 这个对象,他经过推导的类型是
interface IB {
name: string;
age: number;
hobby: string[]
}
而常规的类型检查的赋值,是根据类型的值的集合(条款7),判断两个类型是否是子集的方式判断是否可以进行复制,从上面看 IB 是 IA 集合的一个子集,所以将 IB 赋值给 IA 是有效的;
interface IA {
name: string;
age: number;
}
interface IB {
name: string;
age: number;
hobby: string[];
}
const obj: IB = {
name: 'yunxi',
age: 18,
hobby: ['chouyan', 'hejiu', 'tangtou']
}
const a: IA = obj;
上面的例子表现了额外的类型的检查和常规的类型检查的一个区别,额外的类型检查发生在字面量赋值的时候,他会针对将要赋值给变量的对象字面量进行严格的属性检查,而常规的赋值检查是判断类型是否是子集;
“一个相关”的检查
我们先来看一个例子
interface IC {
name?: string;
age?: number;
}
const c2 = {
hobby: ['tangtou']
}
const c1: IC = c2;
Type '{ hobby: string[]; }' has no properties in common with type 'IC'. 类型‘{hobby:string[];}’与类型‘IC’没有相同的属性。
这个 IC 类型是一个任何属性都可选的一个类型,理论上来说,任何对象类型都是 IC 属性的一个子集,但是对于这种全是可选属性的弱类型,TS 检查器在校验时增加了“一个相关”的类型校验,字面上的意思就是,赋值时,必须存在有一个共同的属性,否则则会报错。而且不能通过中间变量进行转换。
总结
- 对于字面量赋值时,会触发额外属性检查,会严格校验对象的属性是否符合要求;但是存在局限性,可以通过中间变量绕过严格的字面量校验,转为常规的赋值校验(是否为子集);
- 对于一个可选属性的弱类型,TS 增加了一个至少有一个共同类型的判断;
手稿: