type Constructor<T = Record<string, any>> = new (...args: any[]) => T;
type UnionToIntersection<U> = (U extends any ? (k: U) => any : never) extends (k: infer I) => any ? I : never;
// type UnionToIntersection2<U> = U extends any ? (k: U) => any : never
function Mixin<T extends Constructor[]>(...mixins: T): Constructor<UnionToIntersection<InstanceType<T[number]>>>;
// function Mixin<T extends Constructor[]>(...mixins: T): Constructor<UnionToIntersection2<T[number]>>;
function Mixin<T extends Constructor[]>(...mixins: T) {
class Mix { }
const mergeDesc = {};
function copyProperties(target: any, source: any) {
for (let key of Reflect.ownKeys(source)) {
if (key !== 'constructor' && key !== 'prototype' && key !== 'name') {
let desc = Object.getOwnPropertyDescriptor(source, key);
if (desc) {
mergeDesc[key] = mergeDesc[key] || [];
mergeDesc[key].push(desc.value);
Object.defineProperty(target, key, desc);
}
}
}
}
for (let mixin of mixins) {
copyProperties(Mix, mixin);
copyProperties(Mix.prototype, mixin.prototype);
}
for (const key in mergeDesc) {
const fns = mergeDesc[key];
Object.defineProperty(Mix.prototype, key, {
configurable: true,
enumerable: true,
writable: true,
value(...args: any[]) {
const context = this;
fns.forEach(function (fn: Function) {
fn.call(context, ...args);
});
},
});
}
return Mix
}
class Base {
static printBase() {
console.warn(`class<Base>`)
}
same() {
console.log('base same')
}
baseFunc() {
console.log('base func')
}
}
class A {
static printA() {
console.warn(`class<A>`)
}
same() {
console.log('a same')
}
aFunc() {
console.log('a func')
}
}
class B {
static printB() {
console.warn(`class<B>`)
}
same() {
console.log('b same')
}
bFunc() {
console.log('b func')
}
}
class MixClass extends Mixin(Base, A, B) {
constructor() {
super();
}
static print() {
this.printBase() // 报红,不能提示静态类型
this.printA() // 报红,不能提示静态类型
this.printB() // 报红,不能提示静态类型
}
call() {
this.same();
this.baseFunc();
this.aFunc();
this.bFunc();
}
}
MixClass.print();
new MixClass().call();
这个 API 看起来和 Vue2 那套 Class Component 装饰器很像,可以看看它们的类型是怎么实现的?可能他们也没有处理好这个问题,或者没法处理。
https://github.com/vuejs/vue-class-component
https://github.com/facing-dev/vue-facing-decorator
组合优于继承。Vue 新的那套 Composition API ,在类型友好这一点上比 mixin 强太多了。
1赞