为什么 TypeScript 中的 'instanceof' 给我错误“'Foo' 仅指一种类型,但在此处用作值。”?

IT技术 javascript typescript instanceof
2021-02-17 07:54:40

我写了这段代码

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

5个回答

instanceof 使用类,而不是接口。

这是怎么回事

问题是,instanceof是从JavaScript构建,并在JavaScript中,instanceof需要一个的右侧操作数。具体而言,在x instanceof FooJavaScript的执行运行时检查,看是否Foo.prototype在原型链中任何地方存在x

然而,在 TypeScript 中,interfaces 没有发射。这意味着在运行时既不存在Foo也不Foo.prototype存在,所以这段代码肯定会失败。

TypeScript 试图告诉你这永远行不通。Foo只是一个类型,它根本不是一个值!

“我能做什么来代替instanceof?”

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

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

您可能很想从 an 切换interface到 a 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适用于类,而不是接口。认为需要强调这一点。
2021-05-02 07:54:40
所以基本上我没有从答案中得到更好的想法。class?因为你详细。但在你提到“你可能会被诱惑”的同时感到困惑。那么,如果我必须比较所有属性而不是像类型保护文档中那样只比较游泳属性怎么办?
2021-05-08 07:54:40

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

例子

let pet = getSmallPet();

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

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

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

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

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

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