0%

typescript-type-assertion

介绍

今天在做Angular Code Review的时候看到如下一行代码,之前没有见过这种写法,查了一下资料,发现这是TypeScript中的类型断言。

1
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");

其实类型断言我也之前也用过,不过用的是as关键字,如下:

1
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

这两者效果一样。TypeScript中的类型断言有两种写法,一种是<Type>,另一种是as Type。这两种写法是等价的,但是在React中,<Type>会和JSX的语法冲突,所以推荐使用as Type的写法。

既然遇到了,那就顺势学习一下。

什么是类型断言

类型断言是TypeScript的一种特性,它允许开发者告诉编译器某个值的类型是什么,而不是让编译器去推断。类型断言并不会改变值的实际类型,它只是告诉编译器如何处理这个值。

注意:类型断言不是类型转换,它不会改变值的类型。

断言的两种语法

尖括号语法:不适合React等JSX语法的环境,因为尖括号会与JSX标签冲突。

1
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");

as语法:通用性更好,推荐使用。

1
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

类型断言的使用场景

处理DOM元素

document.getElementById为例,它的返回值类型是HTMLElement | null,因此需要使用类型断言来指定具体的类型。

但是HtMLElement这个类型太宽泛了,几乎所有的DOM元素都可以被认为是HTMLElement,当我们的操作需要一个特定的DOM元素类型时,必须进行断言,比如需要对元素调用onFocus方法时,元素的类型必须是HTMLInputElement。这时候就需要断言操作。

1
2
3
4
const inputElement = document.getElementById("myInput") as HTMLInputElement;
inputElement.onfocus = () => {
console.log("Input focused");
};

处理泛型数据

比如开发中常见的JSON数据处理,从JSON字符串解析成对象时,由于JSON字符串表示的对象千变万化,所以使用泛型处理最为合适。

以下代码中T表示最终解析的对象类型,在使用的时候,需要调用者传入具体的类型,比如User类型。

1
2
3
4
5
6
7
8
9
10
function parseJSON<T>(json: string): T {
return JSON.parse(json) as T;
}

interface User {
name: string;
age: number;
}

const user = parseJSON<User>('{"name":"Philip","age":18}');

处理第三方库

在使用第三方库时,可能会遇到一些类型定义不准确的情况,这时候可以使用类型断言来告诉TypeScript如何处理这些类型。
例如,某个第三方库的类型定义文件可能没有包含某个方法或属性,这时候可以使用类型断言来添加这些方法或属性。

1
2
3
4
5
6
7
import { ThirdPartyLib } from 'some-library';

// User类型由库的调用者自行定义,将第三方库返回的用户数据断言为User类型
const users = ThirdPartyLib.getUsers() as User[];

// 使用更安全的类型断言
const safeUsers = ThirdPartyLib.getUsers() as unknown as User[];

const断言

TypeScript 3.4引入了const断言,它可以让我们在声明变量时,告诉编译器这个变量的值是不可变的。使用const断言可以让TypeScript更好地推断类型,尤其是在处理字面量类型时。

1
2
3
4
5
6
7
8
// myString的类型是"Hello, World!",而不是string
const myString = "Hello, World!" as const;

// myArray的类型是readonly [1, 2, 3],而不是number[]
const myArray = [1, 2, 3] as const;

// myObject的类型是{ readonly name: "Alice"; readonly age: 30; },而不是{ name: string; age: number; }
const myObject = { name: "Alice", age: 30 } as const;

const的作用有二:

  1. 将类型字面化,更加精确。
  2. 将对象变为只读,防止意外修改。

比如在项目常用的配置对象,最好的方式是使用const断言,这样可以确保其中的属性不会被意外修改。

1
2
3
4
5
6
7
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
} as const;

// TS2540: Cannot assign to apiUrl because it is a read-only property.
config.apiUrl = "https://api.newexample.com";

好了,今天就到这里了,明天再见!

今天打弹弓打到晚上10点半,理查德和詹姆斯还在打,我说我先走了,今天的任务还没完成,于是我就回来写公众号了,日更不能停!加油!