28
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TypeScript: メソッドが上書きできてしまう仕様を回避する小技

Last updated at Posted at 2020-09-18

この投稿では、TypeScriptでclass構文で定義したメソッドをreadonlyにする小技を紹介します。

実はTypeScriptのメソッドはミュータブルなプロパティ

TypeScriptでもJavaScript同様のclass構文でクラスを定義することができます:

class Person {
}

TypeScriptはJSを拡張していて、フィールドにはreadonly修飾子をつけることができます。これは、フィールドに値を再代入(=上書き)させないようにすることができます。

class Person {
  readonly name: string
  
  constructor(name: string) {
    this.name = name
  }
}

const person = new Person('山田')
person.name = '佐藤' // 再代入しようとする
// エラー: Cannot assign to 'name' because it is a read-only property.(2540)

この例では、nameフィールドがreadonlyなので、これに新しい値の「佐藤」を再代入するコードがコンパイルエラーになっています。

一方、メソッドは次のようにして生やすことができるのですが、メソッドにはreadonly修飾子をつけることができません:

class Person {
  getName() {
    return this.name
  }
}

メソッドを再代入することは一般的に考えにくいですよね。であれば、きっとTypeScript的にはメソッドは常にreadonly扱いのはずです……。

と思っていましたが、実は**メソッドはもろにミュータブル(再代入可)**なのです!

このコードを見てください:

class Person {
    constructor(private readonly name: string) {}
    getName(): string {
        return this.name
    }
}

const person = new Person('山田')

console.log(person.getName())
//=> 山田

// ↓ メソッドを再代入するコード
person.getName = () => '佐藤' // コンパイル通る!

console.log(person.getName())
//=> 佐藤

このコードはPersongetNameメソッドを置き換えていますが、コンパイルエラーになることはありません。

調べてみると、TypeScriptのイシューに「メソッドにもreadonlyがほしい」という要望は上がっているようですが、今のところ対応される様子はありません。

メソッドをreadonlyにする小技

メソッドをreadonlyにする方法としては、ファクトリメソッドを作る方法があります。

まず、クラスを直接newされると、メソッドを守りようがないので、コンストラクタはprivateにします。

その上で、インスタンスを作るためのメソッド(=ファクトリメソッド)を定義します。そして、ファクトリメソッドの戻り値の型をユーティリティ型Readonly<T>にして、返されるインスタンスをイミュータブルな型にします。

先ほどのPersonクラスでこれを実践すると次のようなコードになります:

class Person {
    // コンストラクタを直接呼べないようにする
    private constructor(private readonly name: string) {}

    // ファクトリメソッド
    static readonly new = (name: string): Readonly<Person> => new Person(name)

    getName(): string {
        return this.name
    }
}

こうすることで、メソッドの再代入がコンパイル時に不可能なクラスを作ることができます:

20200918092346@2x.png


最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローお願いします:relieved:Twitter@suin

28
16
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?