一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.
技巧11: 认识到额外属性检查的限制
一. 额外属性检查
如果你将一个对象常量, 赋值给一有类型的变量, 就会启动额外属性检查. 也就是看对象常量有没有其他的额外的属性.
interface Room {
numDoors: number;
ceilingHeightFt: number;
}
const r: Room = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
// ~~~~~~~~~~~~~~~~~~ Object literal may only specify known properties,
// and 'elephant' does not exist in type 'Room'
};
大家可能会感到奇怪: 这明明符合类型兼容(见 技巧4)啊! 为啥会报错呢? 我们先看另一个例子, 如果我们引入一个中间变量, 这个报错又会取消.
const obj = {
numDoors: 1,
ceilingHeightFt: 10,
elephant: 'present',
};
const r: Room = obj; // OK
这个报错能取消, 是因为中间变量obj符合类型兼容的原则.
那么两个例子的区别在于什么地方呢?
在第一个例子, 触发了额外属性检查, 用来捕捉一些重要的错误. 那他会捕捉哪些重要错误呢?
interface Options {
title: string;
darkMode?: boolean;
}
function createWindow(options: Options) {
if (options.darkMode) {
setDarkMode();
}
// ...
}
createWindow({
title: 'Spider Solitaire',
darkmode: true
// ~~~~~~~~~~~~~ Object literal may only specify known properties, but
// 'darkmode' does not exist in type 'Options'.
// Did you mean to write 'darkMode'?
});
这段代码在js运行时不会报错. 但是他也无法正确执行: 正如ts所说: 属性名应该是darkMode, 而不是darkmode.
在我们之前了解到的结构类型检查不会报这个错. 因为Options定义十分宽泛. 宽泛到下面的代码也能通过:
const o1: Options = document; // OK
const o2: Options = new HTMLAnchorElement; // OK
因为documen有title的属性.
但是额外的属性检查能控制这个错误. 给有类型的变量赋值的时候, 额外属性检查: 对象常量不能有额外的属性. 这上面的例子中, document 和 new HTMLAnchorElement 不是对象常量. 所以不会触发额外属性检查, 但是{title, darkmode}
是对象常量:
const o: Options = { darkmode: true, title: 'Ski Free' };
// ~~~~~~~~ 'darkmode' does not exist in type 'Options'...
这也就解释了, 加一个中间变量就不会触发额外属性检查.
const intermediate = { darkmode: true, title: 'Ski Free' };
const o: Options = intermediate; // OK
当你是使用断言的时候不会触发额外属性检查:
const intermediate = { darkmode: true, title: 'Ski Free' };
const o: Options = intermediate; // OK
所以我们又多了一个不使用断言的理由
如果我们不想要额外属性检查, 可以这样写:
interface Options {
darkMode?: boolean;
[otherOptions: string]: unknown;
}
const o: Options = { darkmode: true }; // OK
二. 弱类型的检查
和额外属性检查相似的有: 弱类型检查:
interface LineChartOptions {
logscale?: boolean;
invertedYAxis?: boolean;
areaChart?: boolean;
}
const opts = { logScale: true };
const o: LineChartOptions = opts;
// ~ Type '{ logScale: boolean; }' has no properties in common
// with type 'LineChartOptions'
从结构角度看: LineChartOptions应该包含所有对象. 但是对于像LineChartOptions这样的弱类型, ts增加了一个检查: 值类型和申明类型至少要有一个属性相同.
弱类型检查和额外检查相比:
- 相同的是: 都能找到一般检查找不到的错误
- 不同的是: 弱类型要求type的所有属性都是可选的, 就像LineChartOptions一样. 用中间变量不能绕过弱类型检查