繁体   English   中英

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

[英]Result of TypeScript class factory mixin "is not a constructor function"

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

在此处输入图片说明

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

问题是 mixin 将返回 mixin 添加的内容与原始构造函数T的类型之间的交集。 这在非通用上下文中效果很好,并且生成的合并类将起作用。 问题在于,在另一个 mixin 中, 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)

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

基于TypeScript 文档的示例

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) {
}

这里查看它的实际效果。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM