value instanceof SomeClass 是一个在运行时执行的 JavaScript 检查:当 SomeClass.prototype 位于对象的原型链上时,它返回 true,也就是说这个对象是用 new SomeClass(或其子类)创建的。TypeScript 会在检查内部把 value 收窄为 SomeClass。
对由类创建的对象用 instanceof,对原始值用 typeof。这里两者都需要:instanceof 先分出 Date,再由 typeof 区分剩下的部分。
收窄你自己的类
instanceof 在 true 分支中收窄为该类,在 false 分支中把它排除掉,所以由多个类组成的联合类型可以逐个成员地处理。
子类实例也能通过对父类的检查:如果 Square extends Rect,那么 new Square(2) instanceof Rect 为 true。当各分支的处理不同时,先检查最具体的类。
catch 中的 Error 子类
instanceof 最常见的用法是在 catch 中。在 strict 模式下,捕获到的值是 unknown(什么都可能被抛出),而 instanceof 就是把它变回有类型的错误的办法。
输出:
404: /missing.txt
TypeError: path must be absolute
在 TypeScript 7 支持的所有编译目标(ES2015 及以后)上,class X extends Error 都能配合 instanceof 使用。以前建议在构造函数中调用 Object.setPrototypeOf(this, X.prototype),那针对的是编译到 ES5 的代码,而 TypeScript 7 已经移除了这个目标。
instanceof 不能用于接口或类型
接口和类型别名只为编译器而存在。编译后并没有一个 User 值可供比较,所以 TypeScript 拒绝这样的检查:
编译器会报告 index.ts(8,24): error TS2693: 'User' only refers to a type, but is being used as a value here. 有两种修复方法。一是用类型守卫自己检查结构,也就是一个返回 value is User 的函数:
二是如果这些对象由你的代码创建,就把 User 改成类,用 new 来构造;这样 instanceof 就能用了。类型守卫 页面详细介绍了类型谓词和断言函数。
类型正确不等于实例正确
TypeScript 按结构比较类型:与某个类成员相同的对象字面量,可以赋值给该类类型。instanceof 不看结构,它沿着原型链查找;一个从未用 new 创建的对象,即使编译器接受它是这个类型,也通不过检查。
最后一行很关键。structuredClone、JSON.parse(JSON.stringify(...)) 以及 worker 之间传递的消息,返回的都是不带类原型的普通对象,尽管它们的静态类型仍然可以写作 Point。类实例跨越这样的边界后,要先重新构建(new Point(copy.x, copy.y)),再依赖 instanceof 或调用方法。
instanceof 与原始值
原始值(string、number、boolean 等)不是对象,没有原型链,所以 "hi" instanceof String 是 false。TypeScript 能发现时会标出这个错误:左边是类型为 string 的值时,instanceof 会报错 TS2358:The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. 原始值请用 typeof。
| 值 | instanceof 检查 | 结果 |
|---|---|---|
new Date() | instanceof Date | true |
[1, 2] | instanceof Array | true(但更推荐 Array.isArray) |
new TypeError("x") | instanceof Error | true(子类) |
{ x: 1, y: 2 } | instanceof Point | false(从未构造过) |
Object.create(null) | instanceof Object | false(没有原型) |
"hi" | instanceof String | false(原始值) |
跨 realm 的值
instanceof 比较的是某一个特定的构造函数对象。在另一个 realm(iframe 或 Node 的 vm 上下文)中运行的代码有自己的 Array、Error 和 Date,所以在那里创建的数组,在这里通不过 instanceof Array。同一个 npm 包的两份副本同时出现在 node_modules 中时也会这样:每份副本都有自己的类,一份的实例对另一份做 instanceof 检查会失败。对数组来说,Array.isArray 可以跨 realm 使用。对你自己的类型,检查某个属性(类型守卫,或一个 kind 字段)可以彻底避开这个问题。
常见问题
TypeScript 中如何判断一个对象是否是某个类的实例?
使用 value instanceof ClassName。这是一个运行时检查(普通的 JavaScript),TypeScript 会在 if 内部把 value 收窄为 ClassName。它既适用于 Date、Map、Error 这样的内置类,也适用于你自己的类。
TypeScript 中能对接口使用 instanceof 吗?
不能。TypeScript 编译为 JavaScript 时,接口和类型别名都会被擦除,所以运行时没有可以比较的东西。对接口 User 写 x instanceof User 会报错 TS2693:“'User' only refers to a type, but is being used as a value here.” 改用类型守卫函数检查属性;如果对象是你自己创建的,也可以把 User 改成类。
为什么类型正确的对象,instanceof 却返回 false?
TypeScript 的类型是结构化的:对象字面量 { x: 1, y: 2 } 只要成员相同,就能赋值给 Point 类类型。但 instanceof 检查的是原型链,而这个字面量从来没有用 new Point 创建过,所以结果是 false。经过 JSON、structuredClone 或消息通道传递的类实例也一样,回来的都是普通对象。
TypeScript 中 instanceof 能用于自定义 Error 类吗?
能,只要编译目标是现代版本。先写 class NotFound extends Error {},那么 err instanceof NotFound 就是 true。以前它返回 false 的问题只影响编译到 ES5 的输出,而 TypeScript 7 已不再支持 ES5 目标。
为什么 "hello" instanceof String 是 false?
字符串字面量是原始值,不是对象,所以没有原型链可供检查。只有用 new String() 创建的包装对象,instanceof String 才为 true。TypeScript 会拒绝对类型为 string 的值使用 instanceof(TS2358);原始值请用 typeof value === "string"。