TypeScriptでは、thisの正しい型付けがメソッドチェーンパターンのサポートにとって非常に重要です。これは、メソッドが連続した呼び出しのためにthisを返す場合です。戻り値の型を明示的に指定しないと、TypeScriptはデフォルトで戻り値のオブジェクトを現在のクラスのインスタンスと見なすため、継承時には正しくありません。
この問題を解決するために、戻り値をthisとする**多態的this型(polymorphic this types)**を使用します:
class Builder { value: number = 0; setValue(v: number): this { this.value = v; return this; } } class AdvancedBuilder extends Builder { multiply(factor: number): this { this.value *= factor; return this; } } const b = new AdvancedBuilder().setValue(5).multiply(2); // multiplyが正しく見える
戻り値の型thisは、メソッドチェーンが基底クラスだけでなく、子クラスに対しても正しく型付けされることを保証します。
戻り値の型としてthisの代わりにクラス名(例えば、Builderを返す)を指定することはできますか?これは、子クラスでのメソッドチェーンにどのように影響しますか?
答え:
もしthisの代わりにBuilderを返すと:
class Builder { setValue(v: number): Builder { //... return this; } } class AdvancedBuilder extends Builder { multiply(factor: number): this { // ... return this; } } const a = new AdvancedBuilder().setValue(2).multiply(2); // multiplyが見えない!
setValueの後にmultiplyが利用できないのは、setValueがBuilderを返し、AdvancedBuilderを返さないためです。メソッドのシグネチャでthisを使用することでのみ、メソッドチェーンで正しい型付けが維持されます。
ストーリー
開発チームはフルエントAPIを構築するためのAPIを作成しました。メソッドで基底クラスの型を返していると考え、これが良いプラクティスだとされていました。継承者が登場した後、フルエントAPIのすべての追加メソッドが標準的な呼び出しのチェーンの後に見えなくなってしまいました。結果として、クライアントが拡張を利用できるようにthisへのAPIのリファクタリングが必要でした。
ストーリー
内部のデコレータライブラリで明示的に特定のクラス型を返していました。ユーザーが継承者を利用すると、新しいメソッドへのアクセスを失い、「オブジェクトにはメソッドがありません...」というエラーが発生しました。エラーは新しいサービスとの統合時にのみ発生しました。
ストーリー
開発者が古いC# APIからメソッドをコピーして、クラス名を返すようにTypeScriptのメソッドチェーンを書き換えました。ビルド段階では正常に見えましたが、ユーザーの継承者で新しいメソッドが失われました。tsconfigの厳密なチェックのみがこの問題を迅速に特定し、戻り値をthisに修正する手助けとなりました。