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
    

在这个例子中,我们判断了xy是否完全相等,如果完全相等,那他们的类型肯定也完全相等。而string类型就是xy唯一可能的相同类型。所以在第一个分支里,xy就一定是 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入门手册类型收窄(Narrowing)

Typescript入门手册类型收窄(Narrowing)

Typescript入门手册类型收窄(Narrowing)

Typescript 入门手册之函数类型在 TypeScript 中的应用

Typescript 入门手册之函数类型在 TypeScript 中的应用

Typescript入门手册之引用类型在TypeScript中的应用