TypeScript 的类型断言

Published on

学习教程:TypeScript 的类型断言 - TypeScript 教程 - 网道

简介

TypeScript 提供了“类型断言”这样一种手段,允许开发者在代码中“断言”某个值的类型,告诉编译器此处的值是什么类型。TypeScript 一旦发现存在类型断言,就不再对该值进行类型推断,而是直接采用断言给出的类型。

type T = 'a'|'b'|'c';
let foo = 'a';

let bar:T = foo; // 报错

上面示例中,最后一行报错,原因是 TypeScript 推断变量 foo 的类型是 string ,而变量 bar 的类型是 'a'|'b'|'c' ,前者是后者的父类型。父类型不能赋值给子类型,所以就报错了。

解决方法就是进行类型断言,在赋值时断言变量 foo 的类型。

type T = 'a'|'b'|'c';

let foo = 'a';
let bar:T = foo as T; // 正确

类型断言有两种语法。

// 语法一:<类型>值
<Type>value

// 语法二:值 as 类型
value as Type

为了避免与React 的 JSX 语法(尖括号表示 HTML 元素)冲突,推荐使用语法二。

类型断言的条件

const n = 1;
const m:string = n as string; // 报错

上面示例中,变量 n 是数值,无法把它断言成字符串,TypeScript 会报错。

expr as T

类型断言的使用前提是,值的实际类型 expr 与断言类型 T 必须满足一个条件: exprT 的子类型,或者 Texpr 的子类型

通过连续进行两次类型断言,先断言成 unknown 类型或 any 类型,然后再断言为目标类型,可以断言成一个完全无关的类型。

// expr as unknown as T
// 或者 <T><unknown>expr
const n = 1;
const m:string = n as unknown as string; // 正确

as const 断言

// 后置形式
expr as const

// 前置形式
<const>expr

如果没有声明变量类型, let 命令声明的变量,会被类型推断为 TypeScript 内置的类型之一; const 命令声明的变量,则被推断为值类型常量。

// 类型推断为基本类型 string
let s1 = 'JavaScript';

// 类型推断为字符串 “JavaScript”
const s2 = 'JavaScript';

相当于 const 命令有更强的限定作用,可以缩小变量的类型范围。

let s = 'JavaScript';

type Lang =
  |'JavaScript'
  |'TypeScript'
  |'Python';

function setLang(language:Lang) {
  /* ... */
}

setLang(s); // 报错

函数 setLang() 的参数 language 类型是 Lang,变量s的类型被推断为 string,属于 Lang 的父类型。父类型不能替代子类型,导致报错。

解决办法:

// 1.使用const定义变量
const s = 'JavaScript';

// 2.使用as const
let s = 'JavaScript' as const;
setLang(s);  // 正确

注意,as const断言只能用于字面量,不能用于变量、表达式。

let s1 = 'JavaScript';
let s2 = s1 as const; // 报错

数组字面量使用as const断言后,类型推断就变成了只读元组。

// a1 的类型推断为 number[]
const a1 = [1, 2, 3];

// a2 的类型推断为 readonly [1, 2, 3]
const a2 = [1, 2, 3] as const;

非空断言

对于那些可能为空的变量(即可能等于undefined或null),TypeScript 提供了非空断言,保证这些变量不会为空,写法是在变量名后面加上感叹号!。

function f(x?:number|null) {
  validateNumber(x); // 自定义函数,确保 x 是数值
  console.log(x!.toFixed());
}

function validateNumber(e?:number|null) {
  if (typeof e !== 'number')
    throw new Error('Not a number');
}

非空断言在实际编程中很有用,有时可以省去一些额外的判断。

断言函数

断言函数是一种特殊函数,用于保证函数参数符合某种类型。如果函数参数达不到要求,就会抛出错误,中断程序执行;如果达到要求,就不进行任何操作,让代码按照正常流程运行。

function isString(value:unknown):asserts value is string {
  if (typeof value !== 'string')
    throw new Error('Not a string');
}

assertsis都是关键词,value是函数的参数名,string是函数参数的预期类型。它的意思是,该函数用来断言参数value的类型是string,如果达不到要求,程序就会在这里中断。

如果要断言参数非空,可以使用工具类型NonNullable<T>

function assertIsDefined<T>(
  value:T
):asserts value is NonNullable<T> {
  if (value === undefined || value === null) {
    throw new Error(`${value} is not defined`)
  }
}

工具类型NonNullable<T>对应类型T去除空类型后的剩余类型。