# 二、类型收窄(Narrowing)
# 1.认识类型收窄
function handleType(val: string | number){
if(typeof val === 'string'){
return val.length; // (parameter) val: string
}
return val; // (parameter) val: number
}
这个代码看上去也许没有什么有意思的地方,但实际上,TypeScript 在背后做了很多东西。
TypeScript 要学着分析这些使用了静态类型的值在运行时的具体类型。目前 TypeScript 已经实现了比如 if/else 、三元运算符、循环、真值检查等情况下的类型分析。
在我们的 if 语句中,TypeScript 会认为 typeof val === 'string'
是一种特殊形式的代码,我们称之为类型保护(type guard)
,TypeScript 会沿着执行时可能的路径,分析值在给定的位置上最具体的类型。
# 2.类型保护(type guards)
JavaScript 本身就提供了 typeof 操作符,可以返回运行时一个值的基本类型信息,会返回如下这些特定的字符串:
- "string"
- "number"
- "bigInt"
- "boolean"
- "symbol"
- "undefined"
- "object"
- "function"
typeof
操作符在很多 JavaScript 库中都有着广泛的应用,而 TypeScript 已经可以做到理解并在不同的分支中将类型收窄。在 TypeScript 中,检查typeof
返回的值就是一种类型保护。
function getText(str: number | string): string {
if (typeof str === "number") {
return `${str} isNumber`; // (parameter) str: number
} else {
return `${str} isString`; // (parameter) str: string
}
}
# 3.等值收窄(Equality narrowing)
Typescript 也会使用 switch 语句和等值检查比如 ===、!==、==、!=,去收窄类型。比如:
function getText(a: string | boolean, b: string | null): void {
if (a === b) {
console.log(a);
console.log(b);
// (parameter) a: string
// (parameter) b: string
} else {
console.log(a);
console.log(b);
// (parameter) a: string | boolean
// (parameter) b: string | null
}
}
ts的分析可能是这样的:
- 判断
a
和b
是否完全相等 - 完全相等,
string
类型是a
和b
唯一可能的相同类型。所以a
和b
一定是 string 类型。判断具体的字面量值也能让 TypeScript 正确的判断类型。 - 不等,那就各自安好,不再纠结了~
# 4.四、in 操作符收窄
JavaScript 中有一个 in 操作符可以判断一个对象是否有对应的属性名。TypeScript 也可以通过这个收窄类型。
type Dog = { ww: "1"; mm?: "2" };
type Cat = { mm: "1" };
function inDemo(animal: Dog | Cat): void {
if ("ww" in animal) {
console.log(animal); // (parameter) animal: Dog
} else {
console.log(animal); // (parameter) animal: Cat
}
}
通过 "ww" in animal中,我们可以推断出,animal一定是Dog类型,类型收窄。
而如果有可选属性,Ts也会检测出来:
type Dog = { ww: "1"; mm?: "2" };
type Cat = { mm: "1" };
function inDemo(animal: Dog | Cat): void {
if ("mm" in animal) {
console.log(animal); // (parameter) animal: Dog | Cat
} else {
console.log(animal); // (parameter) animal: Dog
}
}
mm 在 animal中是不能正确收窄的,但如果不在 animal 中,则可以推断出其是 Dog 类型。
# 5.instanceof 收窄
instanceof 也是一种类型保护,TypeScript 也可以通过识别 instanceof 正确的类型收窄:
function instanceofDemo(a: object | number): void {
if (a instanceof String) {
console.log(a);
// (parameter) a: String
} else {
console.log(a);
// (parameter) a: number | object
}
}
# 6.赋值语句(Assignments)
TypeScript 可以根据赋值语句的右值,正确的收窄左值。
let x = Math.random() > 0.5 ? "abc" : 123; // x: string | number
x = 1; // x: number
x = "1"; // let x: string
注意这些赋值语句都有有效的,即便我们已经将 x 改为 number 类型,但我们依然可以将其更改为 string 类型,这是因为 x 最初的声明为 string | number,赋值的时候只会根据正式的声明进行核对。
# 写在最后
本篇文章是《Typescript基础入门》第二篇,收窄是一组比较难理解的思路,这里仅提到部分常见的形式,一起共勉吧!
参考:
欢迎Star⭐️