likes
comments
collection
share

TypeScript 联合类型&类型断言

作者站长头像
站长
· 阅读数 25

联合类型

联合类型就是类型的“或”操作。

我们来看这个例子:

let seven: number;

seven = 7;

seven = 'Seven'; // 会报错,报错信息:不能将类型“Seven”分配给类型“number”

上面代码会报错。当然我们将它改成 any 类型,但是更好的方式是使用联合类型

let seven: number | string;

seven = 7;

seven = 'Seven'; 

使用联合类型的话,只能访问两个类型共有的属性和方法,看下图。

TypeScript 联合类型&类型断言

习题:为变量 animal 声明合适的联合类型

let animal: _____;
animal = 'panda';
animal = 10;
animal.valueOf(0);

A. number | string

B. number | undefined

C. string | undefined

D. undefined | null

解析

我们需要对 animal 声明合适的变量,我们先来分析下面的运算:

animal = 'panda';
animal = 10;

这两步运算需要 animal 声明为 string 或者 number

animal.valueOf();

valueOf 方法是 stringnumberboolean 共有的。故选 A。

类型断言

类型断言,就是断定它一定是这个类型。

我们来看下这个例子:

let seven: number | string;

seven.toFixed(3);

seven.length;

seven.toString();

目前运行上面代码,会报错,看看报错信息。那我们改写代码,让其不报错呢?

可以使用 as 进行类型断言。

let seven: number | string;

(seven as number).toFixed(3);
// 断言成 string 类型
(seven as string).length;

seven.toString();

TypeScript 联合类型&类型断言

习题:关于类型断言,下面描述错误的是?

A. 类型断言可以将一个联合类型的变量,指定为一个更加具体的类型

B. 不能将联合类型断言为不存在的类型

C. 类型断言是一种类型转换。

TypeScript 的类型系统是辅助工具,不会左右我们代码的逻辑,只会尽力的反馈类型给我们,所以类型系统不是一种类型转化。故选 C。

资料:类型断言的两种语法

在之前的学习中我们已经知道如何通过类型断言来预判值的类型,从而访问其属性,我们来看如下代码。

let seven: number | string;

(seven as number).toFixed(3); // 类型断言的第一种语法

<string>seven.length; // 类型断言的第二种语法

上述代码都能通过编译,因为他们都是符合规范的类型断言语法。

这里有一点需要注意,如果在 TypeScript 里面写 JSX 代码,是不能使用 <string>seven.length 来进行断言的,因为此时的 < > 会被识别为 JSX 语法。

资料:断言成 any 类型

看到标题可能很多同学会有疑问,为什么要断言为 any 类型?之前不是说要尽量让类型明确吗。

其实这里并不冲突,为了让代码清晰便于阅读,在使用TypeScript 开发的过程中我们一定要尽量避免声明 any 类型。但是在实际的开发过程中,难免遇到某些场景使得 TypeScript 无法编译通过,这里例举以下两个场景。

方法被调用的时候,实际传入的参数有可能是一个更加具体的值

我们来看如下代码:

function handler (e: Event) {
    let touchEvent = e as TouchEvent;
}

上方代码可以通过编译,但是如下代码却会报错

function handler (e: Event) {
    let $element = e as HTMLElement; // 类型“Event”不可转换为类型“HTMLElement”。类型“Event”中缺少属性“accessKey”。
}

这里我们期望的 $element 是一个 DOM 元素,所以 TS 会尝试去访问 e 是否存在 DOM 元素所必须有的属性,第一个访问的就是 accessKey 属性,因为这里的 e 是 Event 类型,发现访问不到,就会提示错误。

这时如果我们需要强行声明,我们可能就需要 将值断言为 any,类似如下代码:

function handler (e: Event) {
    let $element = e as any as HTMLElement; // 双重断言
}

这里也可以理解为进行了双重断言,最终通过编译。

引入第三方库,造成无法通过编译

假设一个场景,我们引入了一个较老版本的 html2canvas 库,其在 windows 对象上绑定了 html2canvas 函数,我们的用法可能是如下代码:

import './html2canvas';

let $element = document.body;

window.html2canvas($element, canvas => {
    console.log(canvas.toDataURL());
}); // 类型“Window”上不存在属性“html2canvas”

这时候我们可以将 window 断言为 any 类型来避免编译不通过

import './html2canvas';

let $element = document.body;

(window as any).html2canvas($element, canvas=>{
    console.log(canvas.toDataURL());
});