为啥 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
适用于类,而不是接口。
发生了什么
问题在于 instanceof
是 javascript 的构造,在 JavaScript 中,instanceof
期望右侧操作数有一个 值。
具体来说,在x instanceof Foo
中,JavaScript 将执行运行时检查以查看Foo.prototype
是否存在于x
的原型链中的任何位置。
但是,在 TypeScript 中,interface
s 没有发射。这意味着Foo
和Foo.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;
在这种情况下,x
和 y
具有相同的类型,但如果您尝试在其中一个上使用 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 中的类型?