为啥 TypeScript 中的 'instanceof' 会给我错误“'Foo' 仅指一种类型,但在这里被用作值。”?

Posted

技术标签:

【中文标题】为啥 TypeScript 中的 \'instanceof\' 会给我错误“\'Foo\' 仅指一种类型,但在这里被用作值。”?【英文标题】:Why does 'instanceof' in TypeScript give me the error "'Foo' only refers to a type, but is being used as a value here."?为什么 TypeScript 中的 'instanceof' 会给我错误“'Foo' 仅指一种类型,但在这里被用作值。”? 【发布时间】:2018-03-24 00:32:44 【问题描述】:

我写了这段代码

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 似乎不喜欢这样。

【问题讨论】:

查看@4castle 下方的答案。否则,你是对的,我会做到Foo | string Interface type check with Typescript的可能重复 和Check if variable is a specific interface type in a typescript union 的可能副本(我真的不想单枪匹马地敲这个) @Jenny O'Reilly,现在这绝对是可能重复的副本! 【参考方案1】:

您可以使用the in operator narrowing 来检查您需要的元素是否在对象中。

通过这个方法,可以验证x是字符串还是Foo

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

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

instanceof 适用于类,而不是接口。

发生了什么

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

但是,在 TypeScript 中,interfaces 没有发射。这意味着FooFoo.prototype在运行时都不存在,所以这段代码肯定会失败。

TypeScript 试图告诉您这可能永远起作用。 Foo 只是一个类型,根本不是一个值!

“除了instanceof,我还能做什么?”

您可以查看type guards and user-defined type guards。

“但是如果我只是从 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 不会真的告诉你很多关于类型的信息。

【讨论】:

所以基本上我没有从答案中得到更好的想法。班级?因为你详细说明了。但在您提到“您可能受到诱惑”的同时感到困惑。那么,如果我必须比较所有属性,而不仅仅是类型保护文档中的游泳属性呢? 这里的要点是instanceof 使用类,而不是接口。需要强调的想法。【参考方案4】:

如果您希望检查的接口具有不同属性/功能,则在运行时使用接口进行类型检查使用type guards。

例子

let pet = getSmallPet();

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

【讨论】:

如果我了解鸭子并将函数swim()添加到我的Bird接口中会怎样?不是每只宠物都被归类为类型后卫中的鱼吗?如果我有三个接口,每个接口具有三个功能,并且两个与其他接口之一重叠? @Kayz 如果您没有唯一标识接口的属性/函数,则无法真正区分它们。你的宠物实际上可能是一个Duck,你输入guard它变成Fish,但是当你调用swim()时仍然没有运行时异常。建议你创建 1 级通用接口(例如Swimmable)并将swim() 函数移到那里,然后使用((pet as Swimmable).swim 仍然看起来不错。 为了防止类型转换,您可以使用'swim' in pet 条件。它将缩小到必须定义 swim 的子集(例如:Fish | Mammal【参考方案5】:

Daniel Rosenwasser 可能是对的而且花花公子,但我想对他的回答进行修改。完全可以检查 x 的实例,参见代码 sn-p。

但分配 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

【讨论】:

以上是关于为啥 TypeScript 中的 'instanceof' 会给我错误“'Foo' 仅指一种类型,但在这里被用作值。”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Typescript 认为 async/await 返回包装在承诺中的值?

为啥 enumerable: false 不会级联到 TypeScript 中的继承类?

为啥 TypeScript 中的方法链接会导致泛型类型推断失败?

为啥我的 MongoDB ObjectID 没有被识别为 TypeScript 中的类型?

为啥默认禁用 TypeScript 中的不安全/非严格编译器规则?

为啥我在使用 Typescript 的 .net 核心项目中的 NPM 模块出现此错误?