假设我有下面的泛型类,它有两个字段:一个是字符串,另一个是完全任意的泛型--无论我在示例化时希望它是什么:
class Parent<T> {
stringField: String
genericField: T;
constructor(stringField: String, genericField: T) {
this.stringField = stringField;
this.genericField = genericField;
}
}
示例化它可以按预期工作,例如:new Parent("hello", "world").genericField
为字符串提供智能感知,new Parent("hello", 10000).genericField
为数字提供智能感知,等等。
现在假设我想扩展Parent
。除了stringField
和genericField
的值之外,这个新的子类构造函数应该接受一个Parent
构造函数中没有的额外参数。
但是,我不想只是复制粘贴Parent
参数,因为如果我需要更改Parent
,那么这是不可扩展的。因此,我想使用ConstructorParameters
实用程序类型来推断这些冗余参数,并将它们自动传递给super
调用,类似于以下内容:
class Child<G> extends Parent<G> {
numberField: Number;
constructor(numberField: Number, ...params:ConstructorParameters<typeof Parent>) {
super(...params);
this.numberField = numberField;
}
}
但是,这并不像预期的那样工作。在Child
类中对super
的上述调用产生以下编译器错误:Argument of type 'unknown' is not assignable to parameter of type 'G'. 'G' could be instantiated with an arbitrary type which could be unrelated to 'unknown'.
实际上,编写new Child(22, "hello", "world").genericField
并不能提供字符串的智能感知,因为genericField
在这里总是unknown
类型,当我希望它是我传递给它的任何类型时,就像我示例化Parent
一样。
3条答案
按热度按时间ntjbwcob1#
我不知道是否有一个替代的策略或Typescript的特性可以利用,但由于我时间有限,我只会在这里回答为什么你的方法不起作用。
在您的代码中,
ConstructorParameters<typeof Parent>
中的项Parent
不受G
类型参数的约束。因此它的隐式类型是Parent<unknown>
。在
Child<G>
中声明的G
类型参数只约束代码中的两件事:1.子类扩展的类型,即
Parent<G>
1.在ctor中对
super
调用的预期args。这是从#1开始。如果您将鼠标悬停在IDE中的super
上,它将显示:要进一步确认/理解发生了什么,请将
class Parent<T>
更改为class Parent<T extends number>
,并查看错误如何更改。我上面说的话现在应该完全清楚了。解决这个问题的最明显的方法是使用
G
类型参数来约束ConstructorParameters
,例如:但是Typescript不支持这种语法,甚至不支持语义。
也许有一种方法可以使用
infer
或定义一个绑定到Parent
的自定义ConstructorParameters
,但我现在没有时间玩这个。这是一个有趣的问题。我认为TS会有一个解决方案,或者会想要有一个解决方案。我将向TS团队提交问题以获得支持
你可能会得到一个“好主意!”的回答,或者你的问题的解决方案(指向这个SO问题)。如果您确实提交了一个问题,请在您的问题中发布一个链接。
希望比我聪明的人能看到这一点,并提出解决方案。
祝你好运
cunj1qz12#
在TS版本4.3.5中创建了我自己的实用程序类型来解决这个问题。在处理泛型时,我使用它来代替常规的
ConstructorParameters
,它看起来具有很好的可扩展性,不过如果您想确定的话,请务必阅读解释部分。下面是一个带有helper的文件,沿着几个示例playgrounds:泛型构造函数参数.ts
下面是我最初问题中的类的实际情况:Playground链接
这里是如果
Parent
类接受多个泛型:Playground链接为什么会这样
GenericConstructorParameters
有两个参数。它的工作原理是在第一个参数(构造函数,就像ConstructorParameters
的第一个参数)上调用ConstructorParameters
,以获取构造函数参数类型的元组。因为泛型在被ConstructorParameters
提取时变成了unknown
,所以我们用第二个参数提供的类型(替换类型的元组)替换结果元组中的每个unknown
类型。请注意,虽然我只见过
unknown
在泛型类上调用ConstructorParameters
时替换泛型,但我不知道是否在 every 场景中都能保证这一点。所以我希望其他人能证明。如果这是正确的,那么可靠地确定什么是或不是真正的
unknown
是下一步要做的事情。我创建了IsUnknown
实用程序类型(为了便于阅读,在下面进行了扩展)来实现这一点,并得到了来自this SO answer的IfAny
实用程序的帮助。然而,这是另一个不确定这在每一个场景中都有效的例子。如果是这样的话,那么这应该意味着它是可伸缩的,并且无论我的超类使用什么其他类型,包括any
类型,都应该工作。我创建了
UnknownReplacer
实用程序(为了便于阅读,在下面进行了扩展)来完成大部分后续工作。多亏了this SO answer,我知道了如何对可变元组进行递归,通过它,我可以用替换元组中下一个未使用的类型替换构造函数参数中的未知数。最后,
GenericConstructorParameters
类型只是将这些东西很好地 Package 在一起。我不知道这个用例有多小众,或者是否有人以前解决过这个问题,但我希望这是(实际上是正确的),能够帮助其他人解决同样的问题。
寻找反馈!
ffx8fchx3#
这也可以工作,并且更易于管理