Typescript入门手册类型收窄(Narrowing)
Posted 余光、
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Typescript入门手册类型收窄(Narrowing)相关的知识,希望对你有一定的参考价值。
🚀【TypeScript入门手册】记录了出场率较高的Ts概念,旨在帮助大家了解并熟悉Ts
🎉 本系列会持续更新并更正,重点关照大家感兴趣的点,欢迎同学留言交流,在进阶之路上,共勉!
star本项目给作者一点鼓励吧
一、认识Narrowing
试想我们有这样一个函数,函数名为 padLeft:
该函数实现的功能是:如果参数 padding 是一个数字,我们就在 input 前面添加同等数量的空格,而如果 padding 是一个字符串,我们就直接添加到 input 前面。
让我们实现一下这个逻辑:
function padLeft(padding: number | string, input: string)
return new Array(padding + 1).join(" ") + input;
// 运算符“+”不能应用于类型“string | number”和“number”
如果这样写的话,编辑器里 padding + 1
这个地方就会标红,显示一个错误。这是 TypeScript 在警告我们,如果把一个 number
类型 (即例子里的数字 1 )和一个 number | string
类型相加,也许并不会达到我们想要的结果。换句话说,我们应该先检查下 padding
是否是一个 number
:
function padLeft(padding: number | string, input: string)
if (typeof padding === "number")
return new Array(padding + 1).join(" ") + input;
return padding + input;
console.log(padLeft(10, "哈哈"));
这个代码看上去也许没有什么有意思的地方,但实际上,TypeScript 在背后做了很多东西。
TypeScript 要学着分析这些使用了静态类型的值在运行时的具体类型。目前 TypeScript 已经实现了比如 if/else 、三元运算符、循环、真值检查等情况下的类型分析。
在我们的 if 语句中,TypeScript 会认为 typeof padding === number
是一种特殊形式的代码,我们称之为类型保护(type guard)
,TypeScript 会沿着执行时可能的路径,分析值在给定的位置上最具体的类型。
TypeScript 的类型检查器会考虑到这些类型保护和赋值语句,而这个将类型推导为更精确类型的过程,我们称之为收窄 (narrowing)。 在编辑器中,我们可以观察到类型的改变:
function padLeft(padding: number | string, input: string)
if (typeof padding === "number")
return new Array(padding + 1).join(" ") + input;
// (parameter) padding: number
return padding + input;
// (parameter) padding: string
从上图中可以看到在 if 语句中,和剩余的 return 语句中,padding 的类型都推导为更精确的类型。
接下来,我们就介绍 narrowing 所涉及的各种内容。
二、typeof收窄(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
三、等值收窄(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
在这个例子中,我们判断了x
和y
是否完全相等,如果完全相等,那他们的类型肯定也完全相等。而string
类型就是x
和y
唯一可能的相同类型。所以在第一个分支里,x
和y
就一定是 string 类型。判断具体的字面量值也能让 TypeScript 正确的判断类型。
四、in 操作符收窄
JavaScript 中有一个 in 操作符可以判断一个对象是否有对应的属性名。TypeScript 也可以通过这个收窄类型。举个例子,在 “value” in x 中,“value” 是一个字符串字面量,而 x 是一个联合类型:
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 ,我们可以准确的进行类型收窄。
而如果有可选属性,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: Cat
五、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
六、赋值语句(Assignments)
TypeScript 可以根据赋值语句的右值,正确的收窄左值。
let x = Math.random() > 0.5 ? "abc" : 123;
x = 1;
// let x: string | number
console.log(x);
// let x: number
x = "1";
// let x: string | number
console.log(x);
// let x: string
注意这些赋值语句都有有效的,即便我们已经将 x 改为 number 类型,但我们依然可以将其更改为 string 类型,这是因为 x 最初的声明为 string | number,赋值的时候只会根据正式的声明进行核对。
写在最后
本篇文章是《Typescript基础入门》第二篇,收窄是一组比较难理解的思路,这里仅提到部分常见的形式,一起共勉吧!
参考:
关于我:
- 花名:余光
- 邮箱:webbj97@163.com
- csdn:传送门
其他沉淀:
以上是关于Typescript入门手册类型收窄(Narrowing)的主要内容,如果未能解决你的问题,请参考以下文章
Typescript 入门手册之函数类型在 TypeScript 中的应用