TypeScript类工厂mixin的结果“不是构造函数”

时间:2019-06-20 06:18:24

标签: typescript

我正在尝试在TypeScript中使用mixin类。但是,如您在错误输出中清楚看到的那样,mixin应用程序的返回值(以下代码中的Sizeable.mixin())“不是构造函数”,但其类型的第一部分是new (...args: any[]): ,所以有人会认为这是一个构造函数:

enter image description here

有人知道为什么Parent显然是一个构造函数,因为它的类型中包含{ new (...args: any[]):部分,所以可能不允许类对其进行扩展吗?

2 个答案:

答案 0 :(得分:1)

问题在于,mixin将返回由mixin添加的内容与原始构造函数T的类型之间的交集。这在非泛型上下文中效果很好,并且生成的合并类将起作用。问题在于,在另一个混合中,T尚不为人所知,因此交集mxinStuff & T将无法解析为构造函数:

function mixin1<T extends new (... a: any[]) => any> (ctor: T) {
    return class WithMixin1 extends ctor {
        mixin1() {}
    }
}

function mixinMixed<T extends new (... a: any[]) => any> (ctor: T) {
    return class WithMixin2 extends mixin1(ctor) { // Type '{ new (...a: any[]): mixin1<T>.WithMixin1; prototype: mixin1<any>.WithMixin1; } & T' is not a constructor function type.
        mixin2() {}
    }
}

我们可以对mixin1的结果进行某种类型的手术,以使其成为新类的基础类型,然后断言新类是将产生的东西,就像我们扩展了我们的方式一样本来想做扩展的:

function mixin1<T extends new (... a: any[]) => any> (ctor: T) {
    return class WithMixin1 extends ctor {
        mixin1() {}
        static staticMixin1() {}
    }
}
const mixin1BaseType = () => mixin1(class{});    
type Mixin1Type = ReturnType<typeof mixin1BaseType>

function mixinMixed<T extends new (... a: any[]) => {a : string }> (ctor: T) {
    class WithMixin2 extends (mixin1(ctor) as unknown as {
        new (... a: any[]): {a : string } // we pretend this returns the constraint of T
    } & Mixin1Type /* add mixin back in but as instantiated for an empty class*/ ) {
        mixin2() {
            this.a
            this.mixin1()
            WithMixin2.staticMixin1();
        }
        static staticMixin2() {}
    }

    return WithMixin2 as unknown as {
        new (...a: ConstructorParameters<T>): WithMixin2 & InstanceType<T>
    } & T & Mixin1Type & typeof WithMixin2
}

type AnyContructor = new (... a: any[]) => any

let m = mixinMixed(class {
    a: string = "a";
    b: string = "b";
    m() {}
    static staticM() {}
})

let s  = new m();
// instance members are accessible
s.a
s.b
s.m();
s.mixin1();
s.mixin2();

// Staic methods are accessible
m.staticMixin1()
m.staticMixin2()
m.staticM();
console.log(s)

答案 1 :(得分:0)

知道为什么上面的答案如此复杂。你可以只做InstanceType<typeof YourMixedType>

基于 TypeScript docs 的示例

class Sprite {
  name = "";
  x = 0;
  y = 0;

  constructor(name: string) {
    this.name = name;
  }
}

type Constructor = new (...args: any[]) => {};

// This mixin adds a scale property, with getters and setters
// for changing it with an encapsulated private property:

function Scale<TBase extends Constructor>(Base: TBase) {
  return class Scaling extends Base {
    // Mixins may not declare private/protected properties
    // however, you can use ES2020 private fields
    _scale = 1;

    setScale(scale: number) {
      this._scale = scale;
    }

    get scale(): number {
      return this._scale;
    }
  };
}

type Scalable = InstanceType<ReturnType<typeof Scale>>;

const EightBitSprite = Scale(Sprite);
type EightBitSpriteType = InstanceType<typeof EightBitSprite>;

const flappySprite = new EightBitSprite("Bird");

// Now lets use the types!
foo(flappySprite);
bar(flappySprite);
baz(flappySprite);

function foo(sprite: EightBitSpriteType) {
}
function bar(sprite: Scalable) {
}
function baz(sprite: Sprite) {
}

查看实际操作here