学习一些博客的笔记。
原文:Assertion Functions in TypeScript — Marius Schulz
TS系列:TypeScript Evolution — Marius Schulz
如果很急,可以直接看 文章目录:省流结论。
断言函数是一种对类型系统的支持。
TypeScript 3.7 implemented support for assertion functions in the type system.
举个例子,我们想拿到一个id为root的节点,并给它添加点击事件:
const root = document.getElementById("root");
root.addEventListener("click", e => {
/* ... */
});
此时TS会报错Object is possibly null
。因为root是HTMLElement | null
,而null
是没有办法添加点击事件的。因此,我们在添加点击事件前需要保证root是非空 null
、非未定义 undefined
的。
我们有3种方法。
方法1:使用非空断言运算符!
非空断言运算符:!
,告诉TS假定root是非空非未定义。
const root = document.getElementById("root")!;
root.addEventListener("click", e => {
/* ... */
});
root原本的类型是HTMLElement | null
,使用了!
后忽视了null
,TS会只把他当作HTMLElement
。
然而, 使用非空断言!并不是这种情况的正确解决方法 。原因是:!
运算符编译成JS后会自动消失,TS代码不会去判断root是否是null。此时若root是null,还是会报错(没法添加点击事件)。
方法2:内联空检查
先判断root是否为空。
const root = document.getElementById("root");
// 这里的root是 HTMLElement | null
if (root === null) {
throw Error("Unable to find DOM element #root");
}
// 这里的root是 HTMLElement
root.addEventListener("click", e => {
/* ... */
});
这种方法不包含任何特定于TypeScript的语法;以上所有都是语法上有效的JavaScript。
方法3:实现断言函数
如果value是null或undefined,就抛出异常。
function assertNonNullish(
value: unknown,
message: string
) {
if (value === null || value === undefined) {
throw Error(message);
}
}
但是:
const root = document.getElementById("root");
// Type: HTMLElement | null
root;
assertNonNullish(root, "Unable to find DOM element #root");
// Type: HTMLElement | null
root;
// 还是报错:TS认为这里的root还是有可能是null
root.addEventListener("click", e => {
/* ... */
});
TS并不知道我们的断言函数assertNonNullish
已经过滤掉了root是null或undefined的情况。我们需要 显式地 让TS知道此函数assertNonNullish
是断言函数,它断言的值是非null/undefined的。我们可以在返回类型中写关键字:
function assertNonNullish<TValue>(
value: TValue,
message: string
): asserts value is NonNullable<TValue> {
if (value === null || value === undefined) {
throw Error(message);
}
}
其中,asserts value is NonNullable<TValue>
是一个断言签名,它表示:如果函数正常返回(即,没有抛出错误),则value参数的类型是非空的<TValue>
(这里的<TValue>
是泛型)。TS可以用此信息来缩小传递给value参数的表达式的类型。
缩小类型了:
const root = document.getElementById("root");
// Type: HTMLElement | null
root;
assertNonNullish(root, "Unable to find DOM element #root");
// Type: HTMLElement
root;
root.addEventListener("click", e => {
/* ... */
});
NonNullable<T>
是一种条件类型:
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;
把T中的null和undefined去掉了。
如:
NonNullable<HTMLElement>
即HTMLElement
NonNullable<HTMLElement | null | undefined>
即HTMLElement
NonNullable<null | undefined>
即never
省流结论
function assertNonNullish<TValue>(
value: TValue,
message: string
): asserts value is NonNullable<TValue> {
if (value === null || value === undefined) {
throw Error(message);
}
}
const root = document.getElementById("root");
assertNonNullish(root, "Unable to find DOM element #root");
root.addEventListener("click", e => {
/* ... */
});