【问题标题】:Typescript class variables and error: "refers to a value, but is being used as a type here. . Did you mean typeof' ...". Why exactly?打字稿类变量和错误:“指的是一个值,但在这里被用作类型。你的意思是typeof'......”。为什么?
【发布时间】:2021-05-26 02:21:26
【问题描述】:

这适用于 Javascript 和 Typescript:

class A  { /* ... */ }
const B = class extends A { /* ... */ }
var x = new B();
console.log(x instanceof B, x.constructor.name); // true B

但如果我尝试将x 的类型声明为B

var x: B = new B();

我收到打字稿错误:

'B' 指的是一个值,但在这里被用作一个类型。您的意思是“typeof B”吗?

(请注意,如果我将 const B = class extends A { /* ... */ } 替换为简单的 const B = A,我也会得到同样的错误,这是我最初为了使事情尽可能简单而进行的,但在审查时更新了)。

我真的不明白为什么会这样。根据运行时 Javascript x一个B(如上面的console.log所示)。而且所有类都是对象(“值”)无论如何(Typescript 文档本身将类等同于"constuctor objects")。我猜这只是 Typescript 静态分析的一个限制 - 它无法弄清楚“B”是一个构造函数对象,就像“A”一样并追踪它的类型?

而且,我不这么认为,但我想知道,实际上有没有办法在 Typescript 中进行这项工作 - 知道 B 是像 A 一样的构造函数对象并允许将 B 用作类型??


附:我意识到有很多关于 SO 的问题与 “Blah 指的是一个值,但在这里被用作一种类型。你的意思是 'typeof Blah' 吗?”,但我找不到问答像上面一样直接接近它。如果我错过了,请道歉。

【问题讨论】:

  • console.log(x instanceof B, x.constructor.name === 'B'); // true true 如果未编译为打字稿,则实际上返回“true false”。所以“这在 TypeScript 和 JavaScript 中都有效”有点误导。如果它在编译时返回“true true”,则编译器做错了。
  • 这也是 TypeScript 中的 true false。问题只是错误,应该删除该行。
  • @traktor 你的权利,哎呀!该代码被归结为实际有用代码的最低限度。有一个稍微不同的情况,我们得到真实的,真实的。我会更新以删除或更清楚。

标签: javascript typescript


【解决方案1】:

当你写class 声明时

class A { a = 1 }

在 TypeScript 中,您将两个不同的东西带入范围,它们都被命名为A。一个是名为A,它是运行时存在的类构造函数。另一个是名为Atype,对应类实例的TypeScript接口;此类类型在运行时不存在。

还要注意,一个值一个类型,但它本身不是一个类型。并且A 值的类型不是A,因为类构造函数本身不是该类的实例)。相反,它具有typeof A 类型(使用TypeScript's typeof type query operator 表示),类似于new () => A 类型(使用construct signature 表示)。

值和类型都被命名为A 的事实很方便但令人困惑。这很方便,因为它允许您使用一个术语 A 来指代相关但不同的事物,而无需您为每个事物发明新的术语(例如,A 用于类构造函数,而 InstanceA 用于实例类型) .但它令人困惑,因为它可能给人一种错误的印象,即这两件事确实是一件事,或者这两件事之间的关系在某种程度上是名称中固有的,而实际上只是偶然的。


另一方面,当你编写变量声明时

const B = class extends A { b = 2 };

您只是将一个名为B 带入范围。没有名为B 的对应类型。如果你想要这样的类型,你必须自己声明:

type B = InstanceType<typeof B>; 

缺少名为B 的自动类型并不是因为编译器无法确定B 是一个类构造函数。编译器确切地知道B 是什么:

// const B: typeof B

它知道它构造了B 实例:

type WhatBConstructs = InstanceType<typeof B>;
// type WhatBConstructs = B // this "B" is not an actual type name, btw

(这里我使用the InstanceType&lt;T&gt; utility type 来探查B 构造函数的类型以了解其实例类型。)

只是变量声明命名了新值而不是新类型。

microsoft/TypeScript#36348 有一个现有建议,它要求当您将类构造函数绑定到变量时,它还应该使命名类型与实例类型相对应。如果要实现这一点,那么当您编写const B = class extends A {...} 时,您的B 类型就会出现。如果你想看到这种情况发生,你可以去那里给它一个 ?,但我不希望在可预见的未来看到任何变化。


无论如何,现在,如果您手动创建一个名为 B 的值和类型:

const B = class extends A { b = 2 };
type B = InstanceType<typeof B>;

然后事情就会开始按照你想要的方式为你工作:

var x: B = new B(); // okay

Playground link to code

【讨论】:

  • 谢谢,阅读。不确定是否有效果,但我在 OP 中打错了字。我已经用const B = class extends A { /* ... */ } 替换了const B = A,它给出了同样的错误,这就是原始代码的实际样子。道歉。
  • 很棒的答案谢谢! Defs 在那个问题上给了我 ? :)。 type B = A 绝对是进步。不过,事实证明我提到的小更新确实会影响事情:/。我不能说type B = B(循环引用错误)。也不能type TypeOfB = B(B是值错误)。
  • 是的,B 类型只是 IntelliSense 显示的内容;因为它是一个类表达式,所以编译器显示B 是“有用的”,这(imo)比仅仅说(Anonymous class) 或类似InstanceType&lt;typeof B&gt; 的东西更令人困惑。一般来说,您需要type B = InstanceType&lt;typeof B&gt;,这将产生一个在IntelliSense 中看起来像type B = B 但实际上有效的类型。无论如何,我更新了我的答案。
猜你喜欢
  • 2021-01-12
  • 2021-08-02
  • 2020-12-22
  • 2021-07-07
  • 1970-01-01
  • 2020-11-01
  • 2021-07-12
  • 2020-03-23
  • 2019-08-29
相关资源
最近更新 更多