TypeScriptのInterfaceでstaticが使えない件について
Typescriptで初めてジェネリックをちゃんと使って、プロジェクトの共通部分をモジュールとして作るというのをやっていた。
そのときにクラスのStaticなメンバをInterfaceで定義できないというのを知って「まじで?」という気持ちになった。
TypeScriptのgithubでも結構議論されているっぽい。
内容が結構アツくなっていて面白かった。
まず、
「InterfaceでStaticな関数を定義することをサポートしてほしい」というタイトルで以下のコードと共にIssueが挙がっていて、
code: typescript
interface Foo {
public static myStaticMethod(param1: any);
}
これに対して、とあるコントリビューターが
code:typescript
interface JsonSerializableStatic<C extends new (...args) => JsonSerializable<C>> {
fromJson(json: string): JsonSerializable<C>;
}
interface JsonSerializable<C extends new (...args) => any> {
toJson: () => string;
constructor: C;
}
interface A extends JsonSerializable<typeof A> { }
class A implements JsonSerializable<typeof A> {
constructor(readonly id: number, readonly name: string) { }
toJson() { return JSON.stringify(this); }
static fromJson(json: string): A {
const obj = JSON.parse(json);
return new A(obj.id, obj.name);
}
}
const a = new A(1, 'Charlize');
const json = a.toJson();
const y = A.fromJson(json);
console.info(a, json, y);
console.info(new a.constructor(1, 'Theron'));
const m = new A.prototype.constructor(1, 'Charlize Theron');
console.info(m);
「こういう感じで書けば書けるよ」と返事をする。
「まぁ確かに回避策にはなるけど、
code:typescript
interface JsonSerializable {
public static fromJson(obj: any);
public toJson(): string;
}
こう書けた方が明確だよね」と対応する。
ここでちょうどその件でググっていた自分は「わかる〜」と思っていたんだけど、
「いや、ちゃんとした書き方であって、回避策じゃないっすよ。Staticなデシリアライズ関数とインスタンスのシリアライズ関数をクラスごとに実装することで、カプセル化して型安全になってるもん。」と返事がされる。
「いやでもJavaなら8以降で出来るよ」とか、その後も議論は続くんだけど、つまるところInterfaceという名前の通りInterfaceを示すものでクラスの内部構造を示すものではないってことで、必要ならabstract classでやれということでクローズしている。
まぁabstract classでも結局は出来ていなくて、とりあえず現状では上述の回避策のようなコードで実装するより他は無さそう。