Skip to content

TypeScript:Mixins

Take a constructor

Mixins take a class and extend it with new functionality. So we need to define what is a constructor. Easy as:

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

Knowledge

  • TypeScript(그리고 JavaScript) 클래스는 엄격하게 단일 상속만 지원합니다. 1
  • 클래스는 다른 클래스를 확장할 수도 있지만(e.g. class A {} ; class B extends A {}) 인터페이스는 확장할 수 없습니다. (e.g. 에러 코드: interface A {} ; class B extends A {})
  • TypeScript에서 인터페이스는 클래스를 확장할 수도 있지만(e.g. class A {} ; interface B extends A {}) 상속을 포함하는 방식으로만 가능합니다.
    • (중요) 인터페이스가 클래스를 확장할 때 인터페이스는 모든 클래스 멤버(공개 및 비공개)를 포함하지만 클래스 구현은 포함하지 않습니다. 2

Implementation

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function applyMixins(derivedConstructor: any, baseConstructors: any[]) {
  baseConstructors.forEach(baseConstructor => {
    Object.getOwnPropertyNames(baseConstructor.prototype).forEach(name => {
      if (name !== 'constructor') {
        Object.defineProperty(
          derivedConstructor.prototype,
          name,
          Object.getOwnPropertyDescriptor(baseConstructor.prototype, name) ||
            Object.create(null),
        );
      }
    });
  });
}

Usage

다음과 같이 사용하면 하나의 파일에서는 성공한다.

class c0 {
  a: number;

  constructor(a: number) {
    this.a = a;
  }

  get val() {
    return this.a;
  }
}

class c1 extends c0 {
  get first() {
    return this.val + 1;
  }
}

class c2 extends c0 {
  get second() {
    return this.val + 2;
  }
}

class c3 extends c0 {
  b: number;

  constructor() {
    super(300);
    this.b = 200;
  }

  get third() {
    return this.val + 3;
  }
}

interface c3 extends c1, c2 {
  // EMPTY.
}

applyMixins(c3, [c1, c2]);
export default c3;

const temp = new c3();
console.debug(temp.a);
console.debug(temp.first);
console.debug(temp.second);
console.debug(temp.third);
console.debug(temp.b);

Troubleshooting

TS2310: recursively references itself as a base type.

파일 외부에서 사용하기 위해 export하면 다음과 같은 에러가 출력된다.

TS2310: Type 'ReccApi' recursively references itself as a base type.

캐싱문제일 가능성이 높다. IDE 같은걸 사용한다면 재부팅해보자.

See also

Favorite site

References