TypeScript Narrowing 的概念
如何实现下列这个函数?
function padLeft(padding: number | string, input: string) {
}
编译错误的原因:
TypeScript 警告我们将数字添加到数字 | string 可能不会给我们想要的东西,这是对的。 换句话说,我们没有先明确检查 padding 是否是数字,也没有处理它是字符串的情况。
正确的做法:
function padLeft(padding: number | string, input: string) {
if (typeof padding === "number") {
return new Array(padding + 1).join(" ") + input;
}
return padding + input;
}
如果这看起来像是无趣的 JavaScript 代码,那就是重点。除了我们放置的注释之外,这个 TypeScript 代码看起来像 JavaScript。这个想法是 TypeScript 的类型系统旨在使编写典型的 JavaScript 代码尽可能容易。
虽然它看起来可能不多,但实际上这里有很多事情要做。就像 TypeScript 如何使用静态类型分析运行时值一样,它将类型分析覆盖在 JavaScript 的运行时控制流结构上,例如 if/else、条件三元组、循环、真实性检查等,这些都会影响这些类型。
在我们的 if 检查中,TypeScript 看到 typeof padding === "number" 并将其理解为一种称为类型保护(Type guard)的特殊代码形式。 TypeScript 遵循可能的执行路径,我们的程序可以采用这些路径来分析给定位置的值的最具体可能类型。它着眼于这些特殊检查(称为类型保护)和赋值,并将类型细化为比声明的类型更具体的类型的过程称为缩小。在许多编辑器中,我们可以观察这些类型的变化。
比如,在第二个 return 语句里,因为前一个 IF 语句的条件没有满足,所以代码执行到这里时,padding 的类型一定只能是 string 了。
看下面这段代码:
function printAll(strs: string | string[] | null) {
if (typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
} else {
// do nothing
}
}
编译错误:Object is possibly 'null'.
在 printAll 函数中,我们尝试检查 strs 是否为对象以查看它是否为数组类型(现在可能是强调数组是 JavaScript 中的对象类型的好时机)。 但事实证明,在 JavaScript 中,typeof null 实际上是“object”! 这是历史上的不幸事故之一。
有足够经验的用户可能不会感到惊讶,但并不是每个人都在 JavaScript 中遇到过这种情况; 幸运的是,TypeScript 让我们知道 strs 只缩小到 string[] | null 而不仅仅是 string[]。
这可能是我们所谓的“真实性”检查的一个很好的过渡。
Truthiness narrowing
// both of these result in 'true'
Boolean("hello"); // type: boolean, value: true
!!"world"; // type: true, value: true
双感叹号是 Boolean 函数调用的简写形式。
因此上面的代码可以修改成:
function printAll(strs: string | string[] | null) {
if (strs && typeof strs === "object") {
for (const s of strs) {
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
} else {
// do nothing
}
}
使用 in 关键字进行的 narrowing
Javascript 有一个运算符来确定对象是否具有带名称的属性:in 运算符。 TypeScript 将这一点作为缩小潜在类型的一种方式。
例如,使用代码:x 中的“值”。 其中“value”是字符串文字,x 是联合类型。 “true”分支缩小了具有可选或必需属性值的 x 类型,“false”分支缩小到具有可选或缺失属性值的类型。
例子:
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ("swim" in animal) {
return animal.swim();
}
return animal.fly();
}
更多Jerry的原创文章,尽在:"汪子熙":