0

这是我拥有的 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 错误)。

我还想避免交换我的泛型类型(带有约束和默认值),以便我将第一个泛型类型保留为选项并调用第二个。

有没有办法首先定义带有约束的泛型类型,然后设置它们的默认值?

你可以检查这个游乐场链接

4

1 回答 1

0

明显的修复,交换类型参数的顺序,对你不起作用。所以我们不得不求助于不太明显的修复。这里的一般想法是:如果您无法将默认值设置为您想要的值,请将其设置为虚拟值,然后在使用类型时,检查虚拟值并使用您最初想要的默认值。所以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其用作虚拟默认值;除非有人手动指定neverfor O,否则将其用作虚拟对象是一个安全的值。然后OrDefault检查它的参数是否never存在,如果是则返回IOptions<ICalls>

是的,您已经注意到OrDefault' 支票[T] extends [never] ? ... : ...代替T extends never ? ... : .... 我这样做的原因是避免将条件类型分布T在. 由于T是“裸类型参数”,因此当您检查它时T extends never ? ... : ...,编译器会尝试将其解释T为联合,将联合拆分为成员,执行条件,然后将结果合并回联合中。如果传入neverfor T,这将被视为“空并集”,结果将始终为never。我们不想OrDefault<never>成为never,所以我们不想要分布式条件类型。最简单的解决方法是防止检查成为“裸”类型参数,呃,将其“装扮”在一个元组中。 [T]不会分解为工会成分,因此OrDefault<never>将是IOptions<ICalls>所希望的。

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


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

Playground 代码链接

于 2020-03-09T01:30:14.247 回答