Mixin返回一个抽象类的子类,如何指定mixin的返回类型? 简化示例

问题描述

我有一个 mixin,它返回一个抽象类的具体实现。一切都很好地结合在一起,直到我尝试扩展 mixin 返回的类,此时 TypeScript 抱怨我的新子类没有实现抽象方法

我想我明白为什么会发生这种情况,但是我找不到一种方法通知类型系统我没有返回抽象类,而是返回一个具体的子类。

简化示例

这是一个简化示例 (plaground link):

export class PrinterService {
  public print(value: string): void {
    console.log(value);
  }
}

export class FooPrinterService extends PrinterService {
  public print(value: string): void {
    console.log(`FOO: ${value}`);
  }
}

export class BarPrintService extends PrinterService {
  public print(value: string): void {
    console.log(`BAR: ${value}`);
  }
}

export abstract class MixinBase<T extends PrinterService> {
  constructor(
    public service: T,public name: string = 'Abstract') {}

  abstract printName(): void;
}

export type Constructor<T> = new (...args: any[]) => T;

export function Mixin<T extends PrinterService>(ServiceClass: Constructor<T>): Constructor<MixinBase<T>> {
  class MixinClass extends MixinBase<T> {
    constructor(service: T = new ServiceClass(),name = 'Mixin') {
      super(service,name);
    }

    public printName(): void {
      this.service.print(this.name);
    }
  }

  return MixinClass;
}

如果我尝试从混合结果中扩展,这是我想要做的):

class FooExample extends Mixin(FooPrinterService) {}

// Non-abstract class 'FooExample' does not implement inherited abstract member
// 'printName' from class 'MixinBase<FooPrinterService>'.ts(2515)

有趣的是,如果我直接实例化结果,我不会抱怨:

const bar = new (Mixin(BarPrintService))(undefined,'Bar');
bar.printName();

// prints "BAR: bar"

解决方法

示例中 Mixin 函数的返回类型是 Constructor<MixinBase<T>>。 IE。 MixinBase 的构造函数,而不是您想要的 MixinClass。函数本身进行类型检查,因为 MixinClass 可分配给 MixinBase,因为它扩展了它。

但后来 class FooExample extends Mixin(FooPrinterService) {} “认为”它正在扩展 MixinBase 并且失败了,因为 MixinBase 是抽象的,并且至少根据类型,printName 尚未实现.

像这样修改 Mixin,并让 typescript 推断返回类型有效:

export function Mixin<T extends PrinterService>(ServiceClass: Constructor<T>) {
  return class MixinClass extends MixinBase<T> {
    constructor(service: T = new ServiceClass(),name = 'Mixin') {
      super(service,name);
    }

    public printName(): void {
      this.service.print(this.name);
    }
  }
}

(playground link)

打字稿手册在这里有一个关于 mixin 的页面:https://www.typescriptlang.org/docs/handbook/mixins.html