Search code examples
typescriptmixins

How to use classes with mixins as types in typescipt


I have the following code

type GConstructor<T = {}> = new (...args: any[]) => T;
class Sprite {
  name = "";
  x = 0;
  y = 0;

  constructor(name: string) {
    this.name = name;
  }

  setPos(x:number, y:number) {
    this.x = x;
    this.y = y
  }
}
type Positionable = GConstructor<{ setPos: (x: number, y: number) => void }>;

function Jumpable<TBase extends Positionable>(Base: TBase) {
  return class Jumpable extends Base {
    jump() {
      this.setPos(0, 20);
    }
  };
}

const JumpableSprite = Jumpable(Sprite);

function hi(x: JumpableSprite) {
  return 4;
}

the problem is that I get this error

'JumpableSprite' refers to a value, but is being used as a type here. Did you mean 'typeof JumpableSprite'?
Parameter 'x' of exported function has or is using private name 'JumpableSprite'.

I'm pretty sure I don't want typeof JumpableSprite, because I want it to be typed as JumbableSprite not JumbableSprite's class.

Is there a way to use JumpableSprite as a type?


Solution

  • So as @tsecheukfung01 explained in the comments, all you have to do is create a type with the same name as the variable that refers to a class. After you have done this you will see an @typescript-eslint error (if you have @typescript-eslint in your project of course). Acording to the @typescript-eslint docs you should just use an eslint ignore line. The complete working code...

    type GConstructor<T = {}> = new (...args: any[]) => T;
    class Sprite {
      name = "";
      x = 0;
      y = 0;
    
      constructor(name: string) {
        this.name = name;
      }
    
      setPos(x:number, y:number) {
        this.x = x;
        this.y = y
      }
    }
    type Positionable = GConstructor<{ setPos: (x: number, y: number) => void }>;
    
    function Jumpable<TBase extends Positionable>(Base: TBase) {
      return class Jumpable extends Base {
        jump() {
          this.setPos(0, 20);
        }
      };
    }
    
    const JumpableSprite = Jumpable(Sprite);
    // eslint-disable-next-line @typescript-eslint/no-redeclare -- intentionally naming the variable the same as the type
    type JumpableSprite = Sprite & {jump(): undefined};
    
    function hi(x: JumpableSprite) {
      return 4;
    }