《TypeScript实战指南》—2.2.2 交叉类型与联合类型

举报
华章计算机 发表于 2019/06/16 12:28:47 2019/06/16
【摘要】 2.2.2 交叉类型与联合类型通常意义上,我们所说的交叉类型是指将多个字典类型合并为一个新的字典类型。基本类型是不会存在交叉的。比如 number 和 string 是不可能有交叉点的,一个类型不可能既是字符串又是数字。所以当我们使用交叉类型时通常是下面这样:type newType = number & string;let a: newType;interface A { d: n...

2.2.2 交叉类型与联合类型

通常意义上,我们所说的交叉类型是指将多个字典类型合并为一个新的字典类型。

基本类型是不会存在交叉的。比如 number 和 string 是不可能有交叉点的,一个类型不可能既是字符串又是数字。所以当我们使用交叉类型时通常是下面这样:

type newType = number & string;

let a: newType;

interface A {

    d: number,

    z: string,

}

 

interface B {

    f: string,

    g: string,

}

 

type C = A & B

let c: C

这里的 type 关键字是用来声明类型变量的。在运行时,与类型相关的代码都会被移除掉,并不会影响到 JavaScript 的执行。

但如果交叉类型中有属性冲突时,比如下列代码:

let a: newType;

interface A {

    d: number,

    z: string,

}

 

interface B {

    d: string,

    g: string,

}

 

type C = A & B

let c: C

 

c.d = 1;

// [ts]

// Type '1' is not assignable to type 'number & string'.

//  Type '1' is not assignable to type 'string'.

// (property) d: number & string

 

c.d = "123";

// [ts]

// Type '"123"' is not assignable to type 'number & string'.

//  Type '"123"' is not assignable to type 'number'.

// (property) d: number & string

这就非常滑稽了,d 无论如何赋值都不可能通过类型检查。

但当我们正确使用交叉类型时,它可以帮我们合理地将两个不同类型叠加为新的类型,并包含了所需的所有类型。

那么, 如果我们需要一个变量可能是 number,也有可能是 string,该如何表达呢?这也是一个很常见的场景,联合类型便是用于解决这样的问题。比如,下面这段经典的函数:

function padLeft(value: string, padding: any) {

    if (typeof padding === "number") {

        return Array(padding + 1).join(" ") + value;

    }

    if (typeof padding === "string") {

        return padding + value;

    }

    throw new Error(`Expected string or number, got '${padding}'.`);

}

 

padLeft("Hello world", 4); // "    Hello world"

padLeft存在一个问题,padding参数的类型被指定为any。也就是说,我们可以传入一个既不是number也不是string类型的参数,但是TypeScript却不报错:

let indentedString = padLeft("Hello world", true); // 编译阶段通过,运行时报错

在传统的编程语言里,我们可以使用重载来解决这样的问题。

但在 JavaScript的世界里,并没有重载可以使用,手动判断类型操作更常见。这在一定程度上避免了过度设计。

如果我们希望更准确地描述 padding 的类型,那就只能让 padding 既可以是 number 又可以是 string,所以联合类型就非常有必要了。

替代掉 any,我们可以使用联合类型作为padding的参数,如下所示:

function padLeft(value: string, padding: string | number) {

    // ...

}

 

let indentedString = padLeft("Hello world", true);

// [ts] Argument of type 'true' is not assignable to parameter of type 'string | number'.

联合类型表示一个变量可以是几种类型之一。我们用竖线 | 分隔每个类型,所以number | string | boolean表示一个值可以是number、string或boolean。

请记住,如果一个值是联合类型,我们只能访问它们共有的属性。

这说来有点复杂,我们可以来看一下 interface 相关的例子:

interface A {

    a: number,

    b: string,

}

 

interface B {

    b: string,

    c: number,

}

interface C {

    b: string,

    f: number,

}

 

let obj: A | B | C;

 

obj.a = 1;

 

// [ts]

// Property 'a' does not exist on type 'A | B | C'.

// Property 'a' does not exist on type 'B'.

obj.b = '';

在 interface中,联合类型取的是交集,交叉类型取的是并集。这听上去跟名字有些冲突,然而它们在基本类型又不是这样表现的。

如果你仔细阅读上面的例子,还会发现一个问题,就是上面的代码如果直接运行的话是会报错的。所以还需谨记,TypeScript 只会帮你在编译时做类型检查,并不确保你的代码在运行时中的安全。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。