我正在尝试在TypeScript中使用mixin类。但是,如您在错误输出中清楚看到的那样,mixin应用程序的返回值(以下代码中的Sizeable.mixin())“不是构造函数”,但其类型的第一部分是new (...args: any[]):
,所以有人会认为这是一个构造函数:
有人知道为什么Parent
显然是一个构造函数,因为它的类型中包含{ new (...args: any[]):
部分,所以可能不允许类对其进行扩展吗?
答案 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。