カテゴリー
TypeScriptのInterfaceでStaticなクラスメソッドを適用させる方法
※ 当ページには【広告/PR】を含む場合があります。
2022/10/13

以前の当ブログのお題目で、「TypescriptのInterfaceからasync関数(Promise)を利用する」ときの注意点を解説したときがありました。
typescriptのInterfaceは「ポリモーフィズム」や「ダックタイピング」に欠かせない重要な仕組みですが、元々のjavascriptとの兼ね合いもあり、
しかし、interfaceから実装クラスにstaticな関数が指定できないと、たまに困るときがあります。
今回は、どうしてもinterfaceからStatic関数を特定のクラスに実装したい方向けにちょっとニッチなテクニックを紹介してみます。
2つのinterfaceに分離して、別々に適用する方法
まずは基礎的な手法の手っ取り早く解決できるテクニックをやってみましょう。
2つのinterfaceを用意し、以下のソースコードでユニットテストしてみましょう。
//👇通常のimplementでclassに実装させるinterface
interface IInstance {
instanceId: number;
instanceName: string;
instanceMethod(): void;
}
//👇Staticなメソッドと自己インスタンス化(new)を担当するコンストラクタの代役的interface
interface IStatic {
new(instanceId: number, instanceName: string): IInstance;
staticMethod(msg: string): void;
}
const HogeClass: IStatic = class HogeClass implements IInstance {
instanceId: number;
instanceName: string;
constructor(instanceId: number, instanceName: string){
this.instanceId = instanceId;
this.instanceName = instanceName;
}
instanceMethod(){
console.log(`Hello ${this.instanceName} of ID#${this.instanceId} Who Called from Instance Method in TS Class!`);
}
static staticMethod(msg: string) {
console.log(msg);
}
};
//👇通常のクラスメソッドの呼び出し
(new HogeClass(1, 'Hoge')).instanceMethod();
//👇スタティッククラスも利用可
HogeClass.staticMethod('Hello Somebody Who Called from Static Method in TS Class!');
実行結果は、
Hello Hoge of ID#1 Who Called from Instance Method in TS Class!
Hello Somebody Who Called from Static Method in TS Class!
という感じになります。
このやり方では、普通のInterfaceで利用(implements)できるものと、そうでないもの(staticなフィールド)を2つに別々のinterfaceとして分離するのがポイントです。
こうすることで、new演算子が返すクラスインスタンスも分離することができます。
new演算子(関数)は普段は意識することがないですが、暗黙的にクラスのコンストラクタを呼び出して、クラスをインスタンス化した
this
後者のStaticなフィールドを含むinterfaceの中で、
new(...): IInstance
理屈が分かればどうということはないのですが、「interfaceをインスタンス化するものとしないものの2つに分ける」という作業が発生してしまうので、知らない人が見ると不可解なコードに見えてしまう恐れがあり、無用に混乱させてしまうかもしれません。
クラスデコレータでコンストラクタ関数を置換する方法
先ほどと理屈の上では同じですが、
ただし、typescriptでのデコレータ機能自体はまだ試験的なものですので、利用する場合には
tsconfig.json
{
//...中略
"compilerOptions": {
//...中略
"experimentalDecorators": true
}
}
デコレータが利用できるようになったら、以下のソースコードでユニットテストしてみましょう。
//👇クラスデコレータの実装
function staticImplements<T>() {
return <U extends T>(constructor: U) => {constructor};
}
interface IStatic {
staticMethod(msg: string): void;
}
@staticImplements<IStatic>()
class HogeClass {
public static staticMethod(msg: string) {
console.log(msg);
}
}
HogeClass.staticMethod('Hello, Static Method in TS class!');
これを実行すると、
Hello, Static Method in TS class!
となっていればOKです。
ここではいわゆる
implements
@staticImplements
詳しくその用法自体に触れませんが、クラスデコレータはクラスのコンストラクタに適用され、prototype等のクラス定義の書き換える際に使用することができます。
さらに、
この性質を上手く使って、先程説明していたような
早い話が、このデコレータをクラス宣言位置に修飾してあげると、
new HogeClass(...)
HogeClass
コンストラクタの書き換えなんて、なんだか狐につままれるようなテクニックですが、クラスデコレータを知っていると他にも様々なクラスの機能拡張に便利に使うことができるかも知れません。
それだけ、デコレータは強力な機能だということを意識されていると良いでしょう。 めでたしめでたし。
参考サイト
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー