【问题标题】: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 似乎不喜欢这样。

【问题讨论】:

标签: javascript typescript instanceof


【解决方案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 适用于类,而不是接口。

      发生了什么

      问题在于 instanceof 是 JavaScript 的构造,在 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
      

      【讨论】:

        猜你喜欢
        • 2020-06-05
        • 2019-07-05
        • 2018-01-14
        • 2019-10-08
        • 2019-07-30
        • 1970-01-01
        • 2017-08-16
        • 1970-01-01
        相关资源
        最近更新 更多