为什么 TypeScript 中的 instanceof 会给我一个错误“‘ Foo’只是指一个类型,但是在这里被用作一个值。”?

我写了这个代码

interface Foo {
abcdef: number;
}


let x: Foo | string;


if (x instanceof Foo) {
// ...
}

但是 TypeScript 给了我这个错误:

'Foo' only refers to a type, but is being used as a value here.

为什么会这样?我认为 instanceof可以检查我的值是否具有给定的类型,但 TypeScript 似乎不喜欢这样。

205666 次浏览

DR

instanceof使用的是类,而不是接口或类型别名。


TypeScript 想告诉我什么?

问题在于,instanceof是一个来自 JavaScript 的构造,而在 JavaScript 中,instanceof期望右边的操作数是 价值。 具体来说,在 x instanceof Foo中,JavaScript 将执行一个运行时检查,以查看 Foo.prototype是否存在于 x的原型链中的任何位置。

但是,在 TypeScript 中,interface没有发出。type别名也是如此。这意味着 FooFoo.prototype在运行时都不存在,因此这段代码肯定会失败。

TypeScript 试图告诉你这可能是 永远不会工作。Foo只是一个类型,它根本不是一个值!

如果您来自另一种语言,您可能想在这里使用一个类。类 在运行时创建值,但是下面有一些关于这些值的说明,您可能希望阅读。

“如果我仍然想要 type或者 interface,我可以做什么来代替 instanceof?”

你可以查 类型保护和用户定义的类型保护

“但如果我只是从 interface切换到 class呢?”

你可能很想从 interface切换到 class,但是你应该意识到在 TypeScript 的结构类型系统(主要是 基于形状的)中,你可以生成任何与给定类具有相同形状的对象:

class C {
a: number = 10;
b: boolean = true;
c: string = "hello";
}


let x = new C()
let y = {
a: 10, b: true, c: "hello",
}


// Works!
x = y;
y = x;

在这种情况下,xy具有相同的类型,但是如果在其中一个上尝试使用 instanceof,则会在另一个上得到相反的结果。因此,如果您正在利用 TypeScript 中的结构类型,instanceof将不会告诉您关于类型的太多信息。

Daniel Rosenwasser 可能是对的,但我想修改一下他的回答。完全可以检查 x 的实例,请参阅代码片段。

但赋 x = y 也同样容易。现在 x 不是 C 的实例,因为 y 只有 C 的形状。

class C {
a: number = 10;
b: boolean = true;
c: string = "hello";
}


let x = new C()
let y = {
a: 10, b: true, c: "hello",
}


console.log('x is C? ' + (x instanceof C)) // return true
console.log('y is C? ' + (y instanceof C)) // return false

如果您希望检查接口是否具有 与众不同属性/函数,那么在运行时使用接口进行类型检查就是使用 类型防护装置

例子

let pet = getSmallPet();


if ((pet as Fish).swim) {
(pet as Fish).swim();
} else if ((pet as Bird).fly) {
(pet as Bird).fly();
}

当涉及到检查对象是否符合接口签名时,我认为适当的方法是考虑使用“类型谓词”: Https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates

可以使用 运算符缩小检查所需的元素是否在对象中。

使用这个方法,您可以验证 x 是字符串还是 Foo

if ('abcdef' in x) {
// x is instance of Foo
}

instanceof通常是正确答案。

然而,在我的例子中,我使用的是一个基元类型,而 ts 编译器中的 instanceof Numberinstanceof number都出错了:

TS2322: Type 'Number' is not assignable to type 'number'.
TS2693: 'number' only refers to a type, but is being used as a value here.

在使用基元类型时,typeof是正确答案:

if (typeof value === 'number') {this.propertyValue = value}