【问题标题】:Result of TypeScript class factory mixin "is not a constructor function"TypeScript 类工厂 mixin 的结果“不是构造函数”
【发布时间】:2021-03-12 06:27:29
【问题描述】:

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

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

【问题讨论】:

  • 您能否尝试将代码从屏幕截图和问题中移出,并更彻底地解释代码的作用和问题所在?乍一看,虽然我并不完全理解它,但我可以看到 Parent 实际上不是构造函数,它只包含一个构造函数。
  • @apokryfos 它是如何包含构造函数的?那个语法不就是可构造的接口语法吗,也就是说接口类型是构造函数。例如:interface Ctor { new(arg: string): Ctor }。这不是表示可构造的类型吗?
  • 一个可构造的是。但是constructible包含一个构造函数。构造函数本身类似于type Constructor<T = {}> = new (...args: any[]) => T;
  • 我们需要Sizeable.mixin的代码,或者至少需要有类型的声明。

标签: typescript


【解决方案1】:

问题在于,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)

【讨论】:

  • 这确实很可怕。以这种方式编写和编写 mixin 并不友好。例如,使用 mixin 的 mixin 使用 mixin 的 mixin 使用 mixin,并且其中一些 mixin 使用多个 mixin 呢?这就是我在实践中所拥有的(试图从 JS 转换为 TS)。
  • @trusktr 是的,这很丑,我可以尝试把它放在一个实用函数中来隐藏它的一些。如果你有更多继承的 mixin,情况不会变得更糟,因为链中前一个 mixin 的类型已经包含它使用的任何 mixin。
  • 我想效用函数将有一个必需的通用参数。那会更干净,但它会是类型安全的吗?
  • @trusktr 直到我尝试写它才知道.​​.我明天会做一些实验并发布结果..我们会看到
【解决方案2】:

idk 为什么上面的答案如此复杂。你可以做InstanceType&lt;typeof YourMixedType&gt;

基于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

【讨论】:

    猜你喜欢
    • 2021-02-07
    • 2019-03-23
    • 1970-01-01
    • 2013-01-20
    • 2016-11-16
    • 1970-01-01
    • 2014-02-18
    • 2012-01-31
    • 1970-01-01
    相关资源
    最近更新 更多