具有默认值的多个通用约束相互依赖
Multiple generic constraints with defaults relying on each other
这是我的 Typescript interface/class 结构:
interface IBaseOptions {
homeUrl: string;
}
abstract class BaseApp<TOptions extends IBaseOptions = IBaseOptions> {
constructor(
private options: TOptions
) {
// does nothing
}
}
// -----------------------------
interface ICalls {
getValue: () => number;
}
interface IOptions<TCalls extends ICalls = ICalls> extends IBaseOptions {
calls: TCalls;
}
class App<TOptions extends IOptions<TCalls> = IOptions<TCalls>, TCalls extends ICalls = ICalls> extends BaseApp<TOptions> {
-------- (ts2744 error here)
constructor(options: TOptions) {
super(options);
}
}
class SubApp extends App {
// whatever implementation
}
我想提供默认值,这样我就不必为我的选项和调用提供具体类型。我定义类型的方式导致编译错误(ts2744 错误)。
我还想避免交换我的泛型类型(带有约束和默认值),以便我将第一个泛型类型保留为选项并调用第二个。
有什么方法可以先定义带有约束的泛型类型,然后设置它们的默认值吗?
你可以查看这个Playground Link
最明显的解决方法,即交换类型参数的顺序,对您不起作用。所以我们不得不求助于不太明显的修复。这里的一般想法是:如果您不能将默认值设置为您想要的值,请将其设置为虚拟值,然后在稍后使用该类型时,检查虚拟值并使用您最初想要的默认值。所以 Foo<T=Default<U>, U=X> ... T
变成了 Foo<V=DefaultSigil, U=X> ... V extends DefaultSigil ? Default<U> : V
.
这是一种方法:
type OrDefault<T> = [T] extends [never] ? IOptions<ICalls> : T;
class App<O extends IOptions<C> = never, C extends ICalls = ICalls>
extends BaseApp<OrDefault<O>> {
constructor(options: OrDefault<O>) {
super(options);
}
}
在这种情况下,我们使用 never
作为虚拟默认值;除非有人为 O
手动指定 never
,否则用作虚拟值是一个安全的值。然后 OrDefault
检查它的参数是否是 never
,如果是 returns IOptions<ICalls>
是的,您注意到 OrDefault
的支票是 [T] extends [never] ? ... : ...
而不是 T extends never ? ... : ...
。我这样做的原因是为了避免 distributing 跨越 T
的条件类型。由于 T
是 "naked type parameter",当您像 T extends never ? ... : ...
一样检查它时,编译器将尝试将 T
解释为联合,将联合拆分为成员,执行条件,然后然后将结果合并回一个联合体。如果您为 T
传入 never
,这将被视为 "empty union",结果将始终为 never
。我们不希望 OrDefault<never>
成为 never
,因此我们不希望分配条件类型。最简单的解决方法是防止检查成为 "naked" 类型参数,呃,"clothing" 它在一个元组中。 [T]
未分解为并集成分,因此 OrDefault<never>
将根据需要变为 IOptions<ICalls>
。
之后我们不再使用 O
,而是使用 OrDefault<O>
。您应该能够验证这是否按照您想要的方式工作。它笨重且令人费解,但它确实有效。
好的,希望对您有所帮助;祝你好运!
这是我的 Typescript interface/class 结构:
interface IBaseOptions {
homeUrl: string;
}
abstract class BaseApp<TOptions extends IBaseOptions = IBaseOptions> {
constructor(
private options: TOptions
) {
// does nothing
}
}
// -----------------------------
interface ICalls {
getValue: () => number;
}
interface IOptions<TCalls extends ICalls = ICalls> extends IBaseOptions {
calls: TCalls;
}
class App<TOptions extends IOptions<TCalls> = IOptions<TCalls>, TCalls extends ICalls = ICalls> extends BaseApp<TOptions> {
-------- (ts2744 error here)
constructor(options: TOptions) {
super(options);
}
}
class SubApp extends App {
// whatever implementation
}
我想提供默认值,这样我就不必为我的选项和调用提供具体类型。我定义类型的方式导致编译错误(ts2744 错误)。
我还想避免交换我的泛型类型(带有约束和默认值),以便我将第一个泛型类型保留为选项并调用第二个。
有什么方法可以先定义带有约束的泛型类型,然后设置它们的默认值吗?
你可以查看这个Playground Link
最明显的解决方法,即交换类型参数的顺序,对您不起作用。所以我们不得不求助于不太明显的修复。这里的一般想法是:如果您不能将默认值设置为您想要的值,请将其设置为虚拟值,然后在稍后使用该类型时,检查虚拟值并使用您最初想要的默认值。所以 Foo<T=Default<U>, U=X> ... T
变成了 Foo<V=DefaultSigil, U=X> ... V extends DefaultSigil ? Default<U> : V
.
这是一种方法:
type OrDefault<T> = [T] extends [never] ? IOptions<ICalls> : T;
class App<O extends IOptions<C> = never, C extends ICalls = ICalls>
extends BaseApp<OrDefault<O>> {
constructor(options: OrDefault<O>) {
super(options);
}
}
在这种情况下,我们使用 never
作为虚拟默认值;除非有人为 O
手动指定 never
,否则用作虚拟值是一个安全的值。然后 OrDefault
检查它的参数是否是 never
,如果是 returns IOptions<ICalls>
是的,您注意到 OrDefault
的支票是 [T] extends [never] ? ... : ...
而不是 T extends never ? ... : ...
。我这样做的原因是为了避免 distributing 跨越 T
的条件类型。由于 T
是 "naked type parameter",当您像 T extends never ? ... : ...
一样检查它时,编译器将尝试将 T
解释为联合,将联合拆分为成员,执行条件,然后然后将结果合并回一个联合体。如果您为 T
传入 never
,这将被视为 "empty union",结果将始终为 never
。我们不希望 OrDefault<never>
成为 never
,因此我们不希望分配条件类型。最简单的解决方法是防止检查成为 "naked" 类型参数,呃,"clothing" 它在一个元组中。 [T]
未分解为并集成分,因此 OrDefault<never>
将根据需要变为 IOptions<ICalls>
。
之后我们不再使用 O
,而是使用 OrDefault<O>
。您应该能够验证这是否按照您想要的方式工作。它笨重且令人费解,但它确实有效。
好的,希望对您有所帮助;祝你好运!