断言与类型守卫
在可能的情况下,优先使用运行时检查,而不是断言。
断言与类型守卫 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。
让编译器相信的两种方式
当编译器不知道某个值的确切类型时,您有两种选择:对它进行断言(声明其类型),或对它进行守卫(在运行时证明其类型)。两者看起来相似,但安全性却大不相同。
function viaAssert(v: unknown): number {
return (v as number) + 1; // claim
}
function viaGuard(v: unknown): number {
return typeof v === 'number' ? v + 1 : 0; // prove
}
console.log(viaAssert(5), viaGuard(5), viaGuard('x'));断言不会验证
断言是一种没有强制检查的承诺。如果您判断错误,错误就会漏到运行时。断言以安全性换取便利。
const v: unknown = 'hello';
const n = v as number;
console.log(n.toFixed(2)); // runtime error: toFixed is not a function类型守卫会在运行时验证
类型守卫会实际检查该值。如果检查通过,您和编译器都能确定类型正确——类型收窄背后有真实的运行时测试作为依据。
function toFixed2(v: unknown): string {
if (typeof v === 'number') return v.toFixed(2);
return 'N/A';
}
console.log(toFixed2(3.14159), toFixed2('hi'));自定义类型守卫函数
将复杂检查封装到可复用的守卫中,并让它返回 value is T。整个代码库都能从一次经过充分测试的验证中受益。
type Email = { address: string };
function isEmail(v: unknown): v is Email {
return typeof v === 'object' && v !== null &&
'address' in v && typeof (v as any).address === 'string';
}
console.log(isEmail({ address: 'a@b.com' }), isEmail(null));守卫可以组合和复用
由于守卫是普通函数,您可以组合、测试并复用它们。相比之下,断言是分散的声明,无法集中验证。
function isString(v: unknown): v is string { return typeof v === 'string'; }
function isNonEmpty(v: unknown): v is string {
return isString(v) && v.length > 0;
}
console.log(isNonEmpty('hi'), isNonEmpty(''));使用断言注解的断言函数
TypeScript 还提供了断言函数。它们的返回注解使用 asserts 关键字。如果函数正常返回,编译器就会从此处开始收窄参数类型;如果条件不满足,函数就会抛出错误。
function assertNumber(v: unknown): asserts v is number {
if (typeof v !== 'number') throw new Error('not a number');
}
function use(v: unknown): number {
assertNumber(v); // after this line, v is number
return v * 2;
}
console.log(use(21));断言注解与类型谓词的比较
value is T 守卫会返回一个布尔值,供您进行分支判断。asserts value is T 函数会在失败时抛出错误,并在剩余作用域内收窄类型。两者都有运行时依据,不同于普通的 as。
function assertDefined<T>(v: T): asserts v is NonNullable<T> {
if (v === null || v === undefined) throw new Error('missing');
}
const maybe: string | null = 'ok';
assertDefined(maybe);
console.log(maybe.length); // narrowed to string通用断言辅助函数
断言函数非常适合用来定义可复用的前置条件。一个 assert(condition) 辅助函数就可以在整个代码中守护不变量,并在违反条件时尽早抛出错误。
function assert(cond: unknown, msg: string): asserts cond {
if (!cond) throw new Error(msg);
}
function half(n: number): number {
assert(n % 2 === 0, 'must be even');
return n / 2;
}
console.log(half(8));比较安全性
按安全性从高到低排列:类型守卫和断言函数(经过运行时检查)高于普通的 as 断言(未经检查),而普通断言又高于双重断言(后者很容易造成误导)。请选择符合需求的最安全工具。
// Safe: guard
function safe(v: unknown) {
return typeof v === 'string' ? v.trim() : '';
}
console.log(safe(' hi '));选择合适的工具
当您要根据类型进行分支时,请使用类型守卫。当某个前置条件必须成立,否则就应停止执行时,请使用断言函数。只有在无法进行类型收窄时才使用 as(例如进行 DOM 特化)。
function getInput(v: unknown): asserts v is string {
if (typeof v !== 'string') throw new Error('expected string');
}
const raw: unknown = 'name';
getInput(raw);
console.log(raw.toUpperCase());运行时检查更可靠
本课程反复强调的主题是:优先使用运行时检查,而不是断言。守卫和断言函数同时提供编译时类型与运行时安全性,而断言只提供一种声明。
function parsePort(v: unknown): number {
if (typeof v === 'number' && Number.isInteger(v)) return v;
throw new Error('invalid port');
}
console.log(parsePort(8080));快速检查
测试您对断言与类型守卫的理解。
回顾:断言与守卫
您已经学会:
- 类型守卫(
value is T)会在运行时证明类型,并让您进行分支判断。 - 断言函数(
asserts value is T)会在失败时抛出错误,并在之后收窄类型。 - 普通的
as断言只会声明类型,不提供运行时安全性。 - 优先使用经过运行时检查的工具;仅在类型收窄无法处理时使用断言。
下一课程:可选链和空值合并。
function isPositive(v: unknown): v is number {
return typeof v === 'number' && v > 0;
}
console.log(isPositive(5), isPositive(-1));常见问题解答
「断言与类型守卫」课时是免费的吗?
是的 — 「断言与类型守卫」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。
「断言与类型守卫」这节课中我会学到什么?
在可能的情况下,优先使用运行时检查,而不是断言。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 TypeScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「断言与类型守卫」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 TypeScript Academy 课中编写并运行代码吗?
能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。