← 返回题目列表

类型谓词 is 和 asserts 有什么区别?

中等 第 24 / 32 题 更新于 2026/07/29
TypeScript类型收窄类型谓词

简化版

value is Type 是类型谓词,用在返回布尔值的函数上,告诉 TypeScript:当函数返回 true 时,参数可以收窄为某个类型。

asserts value is Type 是断言函数,用在失败时抛错的函数上,告诉 TypeScript:函数正常返回后,参数就可以视为某个类型。前者适合条件判断,后者适合校验失败直接中断。

详细版

function isString(value: unknown): value is string {
  return typeof value === "string";
}

const value: unknown = "hi";
if (isString(value)) {
  value.toUpperCase();
}

断言函数:

function assertString(value: unknown): asserts value is string {
  if (typeof value !== "string") {
    throw new Error("not string");
  }
}

assertString(value);
value.toUpperCase();

类型谓词和断言函数都必须和运行时逻辑一致,否则会骗过编译器,制造隐藏 bug。

完整版教学

一、为什么需要自定义收窄

TypeScript 内置能理解 typeofinstanceof、判别字段等收窄方式。

但业务里常有自己的校验逻辑。

type User = { id: number; name: string };

你希望校验函数不仅返回布尔值,还能让编译器知道校验后的类型。

类型谓词的关键是把运行时判断结果反馈给类型系统。

二、is 用于布尔判断

function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  );
}

使用时:

if (isUser(input)) {
  input.name;
}

isUser(input) 返回 true 的分支里,input 被收窄为 User

三、asserts 用于失败即抛错

function assertUser(value: unknown): asserts value is User {
  if (!isUser(value)) {
    throw new Error("invalid user");
  }
}

使用时不需要 if:

assertUser(input);
input.name;

函数正常返回,说明校验已经通过;否则流程被异常打断。

四、两者适合不同控制流

写法返回适合场景
value is Tbooleanif、filter、分支逻辑
asserts value is Tvoid 或 never 风格参数校验、入口保护、测试断言

例如数组过滤更适合 is

const list: unknown[] = ["a", 1, "b"];
const strings = list.filter(isString);

五、断言函数也可以只断言条件为真

function assert(condition: unknown, message: string): asserts condition {
  if (!condition) throw new Error(message);
}

用法:

assert(user, "user required");
user.id;

这不会指定具体类型,但能排除一些假值路径。

六、不要让类型签名和实现不一致

function isNumber(value: unknown): value is number {
  return true;
}

这会让任意值都被当成 number。TypeScript 相信你的类型谓词,不会自动验证实现是否真实。

实际项目中,复杂数据结构建议配合 Zod、Valibot、io-ts 等运行时 schema。

七、常见误区与追问

  • 误区:写了 value is T 就会自动校验结构。 真正校验取决于函数实现。
  • 误区:asserts 会返回 boolean。 断言函数通常失败抛错,正常返回后收窄。
  • 误区:类型谓词只适合对象。 原始类型、联合类型、类实例都可以。
  • 误区:自定义守卫可以替代所有运行时校验库。 复杂外部输入更适合 schema 校验。
  • 追问:filter 后如何得到 string[]? 传入返回 value is string 的谓词函数。
  • 追问:asserts condition 有什么用? 它能告诉编译器某个条件在后续流程中成立。

八、加强记忆

记忆方式:

is      -> ask a question -> if true narrow
asserts -> make a demand  -> if returns narrow

回答时先分控制流,再给 filter(isString)assertUser(input) 两个例子,最后强调类型谓词必须忠于运行时逻辑。