具有泛型参数的 typeof 类

San*_*ord 6 generics typescript

我正在设计一个小型 API。在其中,开发人员定义一个类和一个配置文件,允许他们在我的系统中使用它。

下面是我的代码的简化:

abstract class Box<T> {
    constructor(protected initialValue: T) {}

    public abstract getDefaultThing(): T;
}

type BoxType = typeof Box;

interface Config {
    boxType: BoxType;
    initialValue: any;
}

function loadConfig(config: Config) {
    // Need to "as any" this so TSC doesn't complain about abstract class
    return new (config.boxType as any)(config.initialValue);
}
Run Code Online (Sandbox Code Playgroud)

Box这里我定义了带有泛型参数的抽象类T。我还提供了一个用于编写配置对象的接口,该对象需要对可实例化 Box 类的引用以及初始值。最后是一个小实现函数,它通过使用 value 创建 Box 的新实例来加载配置initialValue

一个简单的用法如下:

class MyBox extends Box<string> {
    public getDefaultThing(): any {
        return `${this.initialValue} world`;
    }
}

const config: Config = {
    boxType: MyBox,
    initialValue: "hello",
};

const loaded = loadConfig(config);

console.log(loaded.getDefaultThing()); // prints "hello world"
Run Code Online (Sandbox Code Playgroud)

这已经不起作用了 - boxTypeinconfig有一个错误,因为MyBox它比 更窄Box。没关系,我真正想做的是重新定义我的Config接口以进行类型检查initialValueBoxType

type BoxType<T> = typeof Box<T>;

interface Config<T> {
    boxType: BoxType<T>;
    initialValue: T;
}
Run Code Online (Sandbox Code Playgroud)

这不起作用...根本... Box已经是一种类型,并且不能在没有实例化的情况下接受泛型。我知道在某种程度上我正在混合值和类型,但是有没有办法让属性成为对可实例化泛型类的引用,并将其范围缩小到某个泛型类型?

Tit*_*mir 5

如果您不使用typeof Box而是键入接受并返回BoxType<T>一个构造函数签名,那么这一切都会很好地解决TBox<T>

abstract class Box<T> {
    constructor(public initialValue: T) {}

    public abstract getDefaultThing(): T;
}

type BoxType<T> = new (initialValue: T) => Box<T>;

interface Config<T> {
    boxType: BoxType<T>;
    initialValue: any;
}

function loadConfig<T>(config: Config<T>) {
    return new config.boxType(config.initialValue); // no cast 
}

class MyBox extends Box<string> {
  public getDefaultThing(): string { // there was a typo here this was any
      return `${this.initialValue} world`;
  }
}

const config = { // no explcit type needed
  boxType: MyBox,
  initialValue: "hello",
};

const loaded = loadConfig(config); // loaded is Box<string>

console.log(loaded.getDefaultThing()); // prints "hello world"
Run Code Online (Sandbox Code Playgroud)

如果您想loadConfig返回派生类型而不仅仅是抽象类型(即MyBox不是 )Box<string>。您需要一个额外的泛型参数:

abstract class Box<T> {
    constructor(public initialValue: T) {}

    public abstract getDefaultThing(): T;
}

type BoxType<T, TBox extends Box<T>> = new (initialValue: T) => TBox;

interface Config<T, TBox extends Box<T>> {
    boxType: BoxType<T, TBox>;
    initialValue: any;
}

function loadConfig<T, TBox extends Box<T>>(config: Config<T, TBox>) {
    return new config.boxType(config.initialValue); // no cast 
}

class MyBox extends Box<string> {
  public getDefaultThing(): string { // there was a typo here this was any
      return `${this.initialValue} world`;
  }
}

const config = { // no explcit type needed
  boxType: MyBox,
  initialValue: "hello",
};

const loaded = loadConfig(config); // loaded is MyBox

console.log(loaded.getDefaultThing()); // prints "hello world"
Run Code Online (Sandbox Code Playgroud)