打字稿类变量和错误:“指的是一个值,但在此处被用作类型。您的意思是typeof'…”。为什么?

这适用于 Javascript 和 Typescript:

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

但是,如果我尝试声明xas的类型B

var x: B = new B();

我收到打字稿错误:

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

(请注意,我也得到了同样的错误,如果我更换const B = class extends A { /* ... */ }与简单const B = A,这是我原本把事情尽可能简单,但更新的评论)。

我真的不明白为什么会这样。根据运行时 Javascriptx 一个B(如上面的 console.log 所示)。而所有的类都是对象引擎盖下(“值”),反正(打字稿文档本身等同班“constuctor对象”)。我猜这只是 Typescript 静态分析的一个限制——它无法弄清楚“B”是一个像“A”一样的构造函数对象并追踪它的类型?

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


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

回答

当你写这样的class声明时

class A { a = 1 }

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

另请注意,值具有类型,但它本身不是类型。并且A值的类型不是A,因为类构造函数本身不是类的实例)。相反,它具有类型typeof A(使用TypeScript 的typeof类型查询运算符 表示),它类似于类型new () => A(使用构造签名表示)。

值和类型都被命名的事实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

(这里我使用了InstanceType<T>实用程序类型来探查B构造函数的类型以获取其实例类型。)

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

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


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

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

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

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

Playground 链接到代码


以上是打字稿类变量和错误:“指的是一个值,但在此处被用作类型。您的意思是typeof'…”。为什么?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>