use*_*737 8 generics intellisense typescript
假设我有以下泛型类,它有两个字段:一个是字符串,另一个是完全任意的泛型——无论我希望它在实例化时是什么:
class Parent<T> {
stringField: String
genericField: T;
constructor(stringField: String, genericField: T) {
this.stringField = stringField;
this.genericField = genericField;
}
}
Run Code Online (Sandbox Code Playgroud)
实例化它按预期工作,例如new Parent("hello", "world").genericField为字符串new Parent("hello", 10000).genericField提供智能感知,为数字提供智能感知等。
现在假设我想扩展Parent. 这个新的子类的构造应在不存在一个附加参数Parent的构造函数,除了双方的价值观stringField和genericField。
但是,我不想只是复制粘贴Parent参数,因为如果我需要更改Parent. 因此,我想使用ConstructorParameters实用程序类型来推断这些冗余参数并super自动将它们传递给调用,类似于:
class Child<G> extends Parent<G> {
numberField: Number;
constructor(numberField: Number, ...params:ConstructorParameters<typeof Parent>) {
super(...params);
this.numberField = numberField;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这不会按预期工作。于上述呼叫super中的Child类生成以下编译器错误: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.
我不知道 Typescript 是否有可以利用的替代策略或功能,但由于我时间有限,我将在这里回答为什么你的方法不起作用。
在您的代码中,术语ParentinConstructorParameters<typeof Parent>不受G类型参数的约束。因此它的隐式类型是Parent<unknown>。
G中声明的类型参数仅Child<G>限制代码中的两件事:
子类扩展的类型,即Parent<G>
superctor 中调用的预期参数。这是从#1 得出的。如果将鼠标悬停super在 IDE 中,它将显示:
constructor Parent<G>(stringField: String, genericField: G): Parent<G>
Run Code Online (Sandbox Code Playgroud)
要进一步确认/了解发生了什么,请更改class Parent<T>为class Parent<T extends number>并查看错误如何变化。我上面说的现在应该很清楚了。
解决这个问题的明显方法是使用G类型参数来约束ConstructorParameters,例如:
class Child<G> extends Parent<G> {
numberField: Number;
constructor(numberField: Number, ...params:ConstructorParameters<typeof Parent<G>>) {
super(...params);
this.numberField = numberField;
}
}
Run Code Online (Sandbox Code Playgroud)
但 Typescript 不支持这种语法,甚至可能不支持语义。
也许有一种方法可以使用或定义与 相关的infer自定义,但我现在没有时间来使用它。ConstructorParametersParent
这是一个有趣的问题。我认为 TS 会有一个解决方案,或者希望有一个解决方案。我会向 TS 团队提交一个问题来支持
ConstructorParameters<typeof Parent<G>>
Run Code Online (Sandbox Code Playgroud)
你可能会得到一个“好主意!” 回应,或问题的解决方案(指向这个SO问题)。如果您确实提交了问题,请在您的问题中发布该问题的链接。
希望比我聪明的人会看到这一点并提出解决方案。
祝你好运。
在 TS 版本 4.3.5 中创建了我自己的实用程序类型来解决此问题。在处理泛型时,我使用它来代替常规方法ConstructorParameters,并且它看起来非常具有可扩展性,但如果您想确定的话,请务必阅读解释部分。这里是一个文件及其助手,以及几个示例游乐场:
通用构造函数参数.ts
// helpers
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type IsUnknown<T> = unknown extends T ? IfAny<T, never, true> : never;
type UnknownReplacer<T, K> = K extends [infer WithThis, ...infer WithRest] ? T extends [infer ReplaceThis, ...infer ReplaceRest] ? IsUnknown<ReplaceThis> extends never ? [ReplaceThis, ...UnknownReplacer<ReplaceRest, K>] : [WithThis, ...UnknownReplacer<ReplaceRest, WithRest>] : []: T
// GenericConstructorParameters: Takes two arguments
// Arg 1. a constructor
// Arg 2. a tuple of types (one type for each generic in the constructor)
type GenericConstructorParameters<T extends abstract new (...args: any) => any, K> = UnknownReplacer<ConstructorParameters<T>, K>
Run Code Online (Sandbox Code Playgroud)
这是我原来问题中的类的实际操作:Playground Link
Parent如果该类采用多个泛型,情况如下: Playground Link
GenericConstructorParameters有两个参数。它的工作原理是调用ConstructorParameters其第一个参数(构造函数,就像 的第一个参数ConstructorParameters)来获取构造函数参数类型的元组。因为泛型unknown在通过 提取时会变成ConstructorParameters,所以我们将unknown结果元组中的每个类型替换为第二个参数提供的类型(替换类型的元组)。
请注意,虽然我只见过unknown因调用ConstructorParameters泛型类而替换泛型,但我不知道在每种情况下是否都能保证这一点。所以我希望其他人可以验证这一点。
unknown如果这是正确的,那么接下来要做的就是可靠地确定什么是真实的或不是真实的。我创建了IsUnknown实用程序类型(为了便于阅读而在下面进行了扩展)来做到这一点,并IfAny得到了我从这个 SO 答案中获得的实用程序的帮助。然而,这是不确定这是否适用于每个场景的另一个例子。如果确实如此,那么这应该意味着这是可扩展的,并且无论我的超类使用什么其他类型(包括any类型)都应该可以工作。
// helpers
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type IsUnknown<T> = unknown extends T ? IfAny<T, never, true> : never;
type UnknownReplacer<T, K> = K extends [infer WithThis, ...infer WithRest] ? T extends [infer ReplaceThis, ...infer ReplaceRest] ? IsUnknown<ReplaceThis> extends never ? [ReplaceThis, ...UnknownReplacer<ReplaceRest, K>] : [WithThis, ...UnknownReplacer<ReplaceRest, WithRest>] : []: T
// GenericConstructorParameters: Takes two arguments
// Arg 1. a constructor
// Arg 2. a tuple of types (one type for each generic in the constructor)
type GenericConstructorParameters<T extends abstract new (...args: any) => any, K> = UnknownReplacer<ConstructorParameters<T>, K>
Run Code Online (Sandbox Code Playgroud)
我创建了该UnknownReplacer实用程序(为了便于阅读而在下面进行了扩展)来完成大部分的腿部工作。由于这个 SO 答案,我弄清楚了如何对可变参数元组进行递归,并且通过它,我可以用替换元组中下一个未使用的类型替换构造函数参数中的未知数。
type UnknownReplacer<T, K> =
K extends [infer WithThis, ...infer WithRest] // if K is a populated tuple of types (i.e. one for each unknown to replace)
? T extends [infer ReplaceThis, ...infer ReplaceRest] // ...then if T is a populated tuple of types (i.e. from our constructor arguments)
? IsUnknown<ReplaceThis> extends never // ......then check if the first type in T is NOT unknown
? [ReplaceThis, ...UnknownReplacer<ReplaceRest, K>] // .........and if not unknown, return the first type from T with a recursive call on the remaining args from T
: [WithThis, ...UnknownReplacer<ReplaceRest, WithRest>] // .........but if it is unknown, return the first type from K with a recursive call on the remaining args from T and K
: [] // ......but if T is empty (or invalid), return an empty tuple as we've run out of things to check
: T // ...but if K is empty (or invalid), return T as there's nothing we can use to replace anyway
Run Code Online (Sandbox Code Playgroud)
最后,该GenericConstructorParameters类型将事物很好地包装在一起。
我不知道这个用例有多小众,也不知道以前是否有人解决过这个问题,但我希望这是(实际上是正确的)并且能够帮助其他遇到同样问题的人。
寻找反馈!
| 归档时间: |
|
| 查看次数: |
262 次 |
| 最近记录: |