TypeScript 的类型断言
- Published on
简介
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
必须满足一个条件: expr
是 T
的子类型,或者 T
是 expr
的子类型。
通过连续进行两次类型断言,先断言成 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');
}
asserts
和is
都是关键词,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去除空类型后的剩余类型。