在 TypeScript 中是否有一种方法可以组合类 mixin?

3

我有一系列的类混入,在普通的JavaScript中可以使用。

const
  AsFoo = ( superclass ) => class extends superclass {
    get foo(){ return true; }
  },

  AsFooBar = ( superclass ) => class extends AsFoo( superclass ){
    get bar(){ return true; }
  },

  FooBar = AsFooBar( Object ),
  fb = new FooBar();

console.log( fb.foo, fb.bar );
// true, true

然而,当我将它们翻译为TypeScript时,AsFoo( superclass )会出现错误。
type Constructor<T = {}> = new ( ...args: any[] ) => T;

interface Foo {
    foo: boolean;
}

interface FooBar extends Foo {
    bar: boolean;
}

const
  AsFoo = <T extends Constructor>( superclass: T ): Constructor<Foo> & T => class extends superclass implements Foo {
    get foo(){ return true; }
  },
  AsFooBar = <T extends Constructor>( superclass: T ): Constructor<FooBar> & T => class extends AsFoo<T>( superclass ) implements FooBar {
    get bar(){ return true; }
  };

// Type 'Constructor<Foo> & T' is not a constructor function type. ts(2507)

有没有什么方法可以让 TypeScript 与这种模式一起使用? 我不想简单地使用// @ts-ignore: ¯\_(ツ)_/¯

我目前使用的是 TypeScript 3.2.4。


相关 https://github.com/Microsoft/TypeScript/issues/4890 - Shaun Luttin
相关链接:https://github.com/Microsoft/TypeScript/pull/13743 - Shaun Luttin
最终问题在于,AsFooBar 中的 TypeScript 不允许您从泛型类型参数进行扩展。Mixin 是此规则的特殊例外,它们仅在特定情况下允许使用。我建议您采用以下答案中的方法... - Titian Cernicova-Dragomir
@ShaunLuttin,谢谢你提供的链接。我会去查看它们的。 - Jeff Rose
1个回答

3
export type Constructor<T = {}> = new (...args: any[]) => T;
/* turns A | B | C into A & B & C */
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
    ? I
    : never;
/* merges constructor types - self explanitory */
export type MergeConstructorTypes<T extends Array<Constructor<any>>> = UnionToIntersection<InstanceType<T[number]>>;

export function Mixin<T extends Array<Constructor<any>>>(constructors: T): Constructor<MergeConstructorTypes<T>> {
    const cls = class {
        state = {};

        constructor() {
            constructors.forEach((c: any) => {
                const oldState = this.state;
                c.apply(this);
                this.state = Object.assign({}, this.state, oldState);
            });
        }
    };
    constructors.forEach((c: any) => {
        Object.assign(cls.prototype, c.prototype);
    });
    return cls as any;
}

这是我一段时间前玩耍的实现方式,它也合并了每个类的状态,但您可以根据需要自由更改该部分。

使用方法如下...

class A {
    getName() {
        return "hello"
    }
}


class B {
    getClassBName() {
        return "class B name"
    }
}

class CombineAB extends Mixin([A, B]) {
    testMethod() {
        this.getClassBName //is here
        this.getName // is here
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接