メソッド戻り値のthis型とメソッドチェーン
fluent interface
fluent interfaceとは「流れるようなインターフェース」という意味で、method chaining(メソッドの連鎖)という小技を使って、可読性の高いコードを実現するメソッドの作り方のことです。よくドメイン固有言語(DSL)を提供するようなクラスを作るときに使われます。
四則演算ができる変哲もないクラスOperatorを考えます
tsclassOperator {protectedvalue : number;public constructor(value : number) {this.value =value ;}publicsum (value : number): void {this.value +=value ;}publicsubtract (value : number): void {this.value -=value ;}publicmultiply (value : number): void {this.value *=value ;}publicdivide (value : number): void {this.value /=value ;}}constop :Operator = newOperator (0);op .sum (5); // 5op .subtract (3); // 2op .multiply (6); // 12op .divide (3); // 4
tsclassOperator {protectedvalue : number;public constructor(value : number) {this.value =value ;}publicsum (value : number): void {this.value +=value ;}publicsubtract (value : number): void {this.value -=value ;}publicmultiply (value : number): void {this.value *=value ;}publicdivide (value : number): void {this.value /=value ;}}constop :Operator = newOperator (0);op .sum (5); // 5op .subtract (3); // 2op .multiply (6); // 12op .divide (3); // 4
演算ごとにステートメントを分ける必要があります。このような場合メソッドチェーンを使って処理を連続させることができます。
tsclassOperator {protectedvalue : number;public constructor(value : number) {this.value =value ;}publicsum (value : number):Operator {this.value +=value ;return this;}publicsubtract (value : number):Operator {this.value -=value ;return this;}publicmultiply (value : number):Operator {this.value *=value ;return this;}publicdivide (value : number):Operator {this.value /=value ;return this;}}constop :Operator = newOperator (0);op .sum (5).subtract (3).multiply (6).divide (3); // 4
tsclassOperator {protectedvalue : number;public constructor(value : number) {this.value =value ;}publicsum (value : number):Operator {this.value +=value ;return this;}publicsubtract (value : number):Operator {this.value -=value ;return this;}publicmultiply (value : number):Operator {this.value *=value ;return this;}publicdivide (value : number):Operator {this.value /=value ;return this;}}constop :Operator = newOperator (0);op .sum (5).subtract (3).multiply (6).divide (3); // 4
op.sum(), op.subtract(), op.multiply(). op.divide()の戻り値の型をOperatorに変更しました。これによりメソッドチェーンが可能になりました。
ここで、このクラスOperatorを拡張して累乗の計算を追加したいとします。すると新しいクラスNewOperatorは次のようになるでしょう。
tsclassNewOperator extendsOperator {public constructor(value : number) {super(value );}publicpower (value : number):NewOperator {this.value **=value ;return this;}}
tsclassNewOperator extendsOperator {public constructor(value : number) {super(value );}publicpower (value : number):NewOperator {this.value **=value ;return this;}}
ですが、このクラスでは次の演算ができません。
tsconstop :NewOperator = newNewOperator (2);Property 'power' does not exist on type 'Operator'.2339Property 'power' does not exist on type 'Operator'.op .power (3).multiply (2).(3); power
tsconstop :NewOperator = newNewOperator (2);Property 'power' does not exist on type 'Operator'.2339Property 'power' does not exist on type 'Operator'.op .power (3).multiply (2).(3); power
これはop.multiply()の戻り値がOperatorだからです。Operatorにはpower()というメソッドがないためこのような問題が発生します。
このようなとき、戻り値にthisを設定することができます。上記クラスの戻り値のOperator, NewOperatorをすべてthisに置き換えると問題が解消されます。
tsclassOperator {protectedvalue : number;public constructor(value : number) {this.value =value ;}publicsum (value : number): this {this.value +=value ;return this;}publicsubtract (value : number): this {this.value -=value ;return this;}publicmultiply (value : number): this {this.value *=value ;return this;}publicdivide (value : number): this {this.value /=value ;return this;}}classNewOperator extendsOperator {public constructor(value : number) {super(value );}publicpower (value : number): this {this.value **=value ;return this;}}constop :NewOperator = newNewOperator (2);op .power (3).multiply (2).power (3); // 4096
tsclassOperator {protectedvalue : number;public constructor(value : number) {this.value =value ;}publicsum (value : number): this {this.value +=value ;return this;}publicsubtract (value : number): this {this.value -=value ;return this;}publicmultiply (value : number): this {this.value *=value ;return this;}publicdivide (value : number): this {this.value /=value ;return this;}}classNewOperator extendsOperator {public constructor(value : number) {super(value );}publicpower (value : number): this {this.value **=value ;return this;}}constop :NewOperator = newNewOperator (2);op .power (3).multiply (2).power (3); // 4096