javascript - 在通过构造函数进行泛型初始化期间类型未正确断言
问题描述
我在 TypeScript 中遇到了一个小问题,我不确定是我输入错误,还是 TypeScript 无法正确断言类型。让我展示重现问题所需的所有代码:
interface IRawFoo { type: string };
type FooConstructor = new (...args: any[]) => BaseFoo;
class BaseFoo { }
class Ext1Foo extends BaseFoo { }
class Ext2Foo extends BaseFoo { }
const FooConstructorMap = {
"ext1": Ext1Foo,
"ext2": Ext2Foo,
}
function getConstructor(rawFoo: IRawFoo): FooConstructor {
return FooConstructorMap[rawFoo.type];
}
function getInstance(rawFoo: IRawFoo): BaseFoo {
const fooConstructor = getConstructor(rawFoo);
return initializeFoo(rawFoo, fooConstructor);
}
function initializeFoo<T extends FooConstructor>(rawFoo: IRawFoo, constructor: T): InstanceType<T> {
const newFoo = new constructor();
/* Doing stuff for each FOO instance. */
return newFoo; // TS ERROR HERE
}
/* USAGE EXAMPLE .*/
const ext1RawFoo : IRawFoo = { type: "ext2" };
const ext1Foo = initializeFoo(ext1RawFoo, Ext1Foo); // <- In this example, ext1Foo is a "Ext1Foo".
现在,让我试着快速解释一下:
- 我有
BaseFoo
和 n 个扩展它的类; - 每 n 个类的构造函数通过一个对象映射;
- 我必须能够调用
getInstance
which 返回 aBaseFoo
。最终,我将能够使用instanceof()
断言返回元素的特定类型; - 我必须能够
initializeFoo
直接调用传递构造函数,并且具有没有任何断言的返回类型;
当前代码在以下return
语句中给了我这个错误initializeFoo
:
Type 'BaseFoo' is not assignable to type 'InstanceType<T>'.ts(2322)
不过,我不确定为什么,因为我写的代码对我来说很有意义。在initializeFoo
中,我断言T
extends FooConstructor
,因此T
是一个具有构造函数的对象,该构造函数返回BaseFoo
. 然后,我还说initializeFoo
返回一个InstanceType<T>
. 由于 instanciatedT
返回 a BaseFoo
due to T extends FooConstructor
,我认为InstanceType<T> extends BaseFoo
可以做出断言。但是,TypeScript 没有。
在这一点上,我不知道我是否写错了,是否有更好的方法来写这个,或者 TypeScript 不够聪明,无法做出这样的假设。
我知道我可以这样写签名initializeFoo
:
function initializeFoo<T extends FooConstructor>(rawFoo: IRawFoo, constructor: T): BaseFoo
但是,当我写这个时:
const ext1Foo = initializeFoo(ext1RawFoo, Ext1Foo);
ext1Foo
类型将是BaseFoo
而不是Ext1Foo
.. 我不喜欢。
最后,我知道我可以通过以下方式解决这个问题:
return newFoo as InstanceType<T>
但是使用as
,在我看来,由于编写了错误的代码,我在作弊。
谢谢!
解决方案
所以这里棘手的部分是拥有的含义T extends FooConstructor
。由于FooConstructor
被定义为new (...args: any[]) => BaseFoo
,扩展FooConstructor
的东西也是一个返回BaseFoo
;的函数。它只是可能需要更具体的参数,具有更多属性等。您想要的是“返回扩展类型BaseFoo
的构造函数”,而不是“扩展返回构造函数类型的类型的构造函数BaseFoo
”。
以下是如何重新定义构造函数类型并初始化函数以按预期工作:
type FooConstructor<T extends BaseFoo = BaseFoo> = new (...args: any[]) => T;
// ...
function initializeFoo<T extends BaseFoo>(rawFoo: IRawFoo, constructor: FooConstructor<T>): T {
const newFoo = new constructor();
/* Doing stuff for each FOO instance. */
return newFoo;
}
推荐阅读
- r - 为什么 tidyverse group_by 在 R 更新后表现出意外
- linux - 在 Dockerfile 中多次使用相同的变量
- go - sync.Map 似乎不安全的并发读/写
- pythonanywhere - 在 PythonAnywhere 中添加到白名单
- javascript - 如何在 Vuejs 模板中调用带参数的 Laravel 模型函数?
- vhdl - Xilinx 中的 case 语句错误(case-when)
- python - 使用后序二叉树遍历计算子树数
- python - 在遍历 pandas Series 时,使用 Series 的每个成员查询 SQLite 数据库
- c# - MongoDB 使用 C# 驱动程序替换数组内的数组
- java - 如何将对象添加到java中的对象?