🐾

typescriptの型定義ファイルでMixinを定義したかった

2024/06/05に公開

はじめに

typescript/javascript万年初心者です。
これはタイトルの通りで、うまいやり方がわからなかったという内容です。
(ご存知の方がいれば教えていただきたい。)

型定義したい理由

jsを使用しているプロジェクトを開発している中で、ts-checkを使用して解析してもらいたいと思いました。(最近存在を知ったので使いたいだけ)
外部のライブラリを使用しているが,そのライブラリの型定義やソースは非公開でドキュメントだけ存在するのでその仕様をd.tsの型定義ファイルとして用意したいのです。

問題設定

jsでは以下のような実装をしている。

class AAA extends ZZZMixin(BBB) {

    hoge() {
        console.log(ZZZMixin.hoge);
        this.zzzfunc();
    }
}
  • ZZZMixinはstaticな変数を持っている。(ZZZMixin.hoge)
  • ZZZMixinをmixinしたクラスはzzzfuncを呼べる。
  • もちろんmixinされたBBBクラスのプロパティにもアクセスできる。

以上をd.tsで定義してみたい

tscで生成してみる

セットアップ

mkdir mixintest
cd mixintest
npx tsc --init

tsconfig.js

{
    "include": ["src"],
    "compilerOptions": {
        "emitDeclarationOnly": true,
        "declaration": true,
        "outDir": "./out",
        "target": "esnext",
        "allowJs": true,
        "checkJs": true
    }
}

トランスパイル元のjsを用意

const ZZZMixin = superClass =>
    class extends superClass {
        static hoge = 'Hoge';
        zzzfunc() {}
    };

トランスパイルで型定義ファイルを生成と結果

以下コマンドで生成する。tsconfig.jsonにしたがって、./out以下にzzzmixin.d.tsが生成される。

npx tsc

結果
tscコマンドで以下のようなファイルが生成された。
zzzfuncは補完されるのでいいが、ZZZMixin.hogeでアクセスすることはできないしstaticでもない。

declare function ZZZMixin(superClass: any): {
    new (): {
        [x: string]: any;
        zzzfunc(): void;
    };
    [x: string]: any;
    hoge: string;
};

妥協した結果

mixinの中身はBBBしか使用していないので以下で妥協している。
決め打ちでBBBをextendsしたクラスを定義しているので、Mixinとは名ばかりの定義になってしまった。

declare class Apple {
    apple(): ()=>string
}


export class ZZZMixin extends Apple {
    static hoge: "hoge"
    zzzfunc: ()=>void
}

type Constructor = new (...args: any[]) => {};
export function ZZZMixin<TBase extends Constructor>(Base: TBase): typeof ZZZMixin

その他参考

Discussion