尝试使用基本 class 方法时 Typescript mixin 失败

Typescript mixin fails when trying to use base class method

MCVE

我想创建一个 mixin,它可以使用它所混入的 class 的方法。然而,这是行不通的。

////////////////////
// Simple class
class User {  
  sayHello(msg:string){
      console.log(msg)
  }
}
// Needed for all mixins
type Constructor<T = {}> = new (...args: any[]) => T;
////////////////////
// A mixin that adds a method that uses a base class method
function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {    
    activate() {      
      (Base as unknown as User).sayHello("activated")
    }
  };
}
////////////////////
// Using the composed class
////////////////////
const ActivableUser = Activatable(User)
const activableUser = new ActivableUser()
activableUser.activate()

运行 失败 [ERR]: Base.sayHello is not a function

有什么方法可以创建一个可以访问基础 class 方法和属性的混入?

activate方法中的

Base是class构造函数。它不是一个实例。 activate 是实例上的方法。

但由于 activate 是一个实例方法,您可以只使用 this 代替。

只需将 Base 替换为 this 就可以了。

function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {    
    activate() {      
      (this as unknown as User).sayHello("activated")
    }
  };
}

Playground


此外,如果您告诉 mixin 它依赖于哪些实例方法,您可以通过 unknown 摆脱那个令人讨厌的转换。

function Activatable<
  TBase extends Constructor<{ sayHello(msg: string): void }>
>(Base: TBase) {
  return class extends Base {    
    activate() {      
      this.sayHello("activated")
    }
  };
}

Playground

Fot 完整性,基于@Alex Wayne 的回答和他在评论中的附加指导,这是我真正想要的解决方案,即用它必须实现的接口来约束基础 class并在 mixin 函数中从基础 class 中要求此接口。

Playground

////////////////////
// Interface that our base class has to implement
interface CanSayHello {
  sayHello(msg:string):void
}

////////////////////
// Base class
class User implements CanSayHello {  
  sayHello(msg:string){
      console.log(msg)
  }
}

////////////////////
// Needed for all mixins
type Constructor<T = {}> = new (...args: any[]) => T;

////////////////////
// A mixin that adds a method that uses a base class method
function Activatable<TBase extends Constructor<CanSayHello>>(Base: TBase) {
  return class extends Base {    
    activate() {      
      this.sayHello("activated")
    }
  };
}

////////////////////
// Creating the composed class
const ActivableUser = Activatable(User)
const activableUser = new ActivableUser()

////////////////////
// Using the composed class
activableUser.activate()