【问题标题】:Multiple generic constraints with defaults relying on each other具有相互依赖的默认值的多个通用约束
【发布时间】:2020-03-03 19:04:03
【问题描述】:

这是我拥有的 Typescript 接口/类结构:

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

【问题讨论】:

  • this 适合你吗?
  • @jcalz 基本上它确实如此,即使它有点令人费解。将您的解决方案放在答案中,以便我接受。并解释一下为什么选择在条件类型中将Tnever 放在方括号中?

标签: typescript default-value generic-constraints


【解决方案1】:

明显的修复,交换类型参数的顺序,对你不起作用。所以我们不得不求助于不太明显的修复。这里的一般想法是:如果您无法将默认值设置为您想要的值,请将其设置为虚拟值,然后在使用类型时,检查虚拟值并使用您最初想要的默认值。所以Foo&lt;T=Default&lt;U&gt;, U=X&gt; ... T 变成了Foo&lt;V=DefaultSigil, U=X&gt; ... V extends DefaultSigil ? Default&lt;U&gt; : 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,如果是则返回IOptions&lt;ICalls&gt;

是的,您已经注意到 OrDefault 的支票是 [T] extends [never] ? ... : ... 而不是 T extends never ? ... : ...。我这样做的原因是为了避免distributing 跨越T 的条件类型。由于T 是“裸类型参数”,当您像T extends never ? ... : ... 一样检查它时,编译器将尝试将T 解释为联合,将联合拆分为成员,执行条件,然后将结果合并回一个工会。如果您将never 传递给T,这将被视为“空联合”,结果将始终为never。我们不希望OrDefault&lt;never&gt; 成为never,所以我们不希望分配条件类型。最简单的解决方法是防止检查成为“裸”类型参数,嗯,将其“装扮”在一个元组中。 [T] 不会分解为工会成员,因此 OrDefault&lt;never&gt; 将根据需要变为 IOptions&lt;ICalls&gt;

之后我们不再使用O,而是使用OrDefault&lt;O&gt;。您应该能够验证这是否按您想要的方式工作。它笨重且令人费解,但它确实有效。


好的,希望对您有所帮助;祝你好运!

Playground link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多