Rubyのattr_reader的なことをTypeScriptでやるならreadonly

# はじめに

TypeScriptで「クラスにgetterは作るけどsetterを作らない」とき、Rubyの`attr_reader`っぽくシンプルに書きたいなーと思って試したメモです。

# TL;DR

```ts:TypeScript
class Hoge {
  constructor(readonly value: Type) { }
}
```

```rb:Ruby
class Hoge
  attr_reader :value
  def initialize(value)
    @value = value
  end
end
```

なんとTypeScriptの方が短く書ける！



# やりたいこと
`Item`クラスを、こんな風に使いたい。

```ts:TypeScript
const item = new Item('Apple')
console.log(item.name) // Apple
item.name = 'Orange' // エラー
```

# TypeScriptでのイマイチな例
## 冗長なprivate

検索するとよく見る例ですね。Javaにかなり近い雰囲気。

```ts:TypeScript
class Item {
  private _name: string
  constructor(name: string) {
    this._name = name
  }
  get name(): string {
    return this._name
  }
}

const item = new Item('Apple')
console.log(item.name) // Apple
item.name = 'Orange' // Cannot assign to 'name' because it is a read-only property.
```

うーん、、ちょっとつらい。
やりたいことは「コンストラクタの引数をそのまんまgetしたい」だけなのに、`this`が必須だったり、名前が被るから`_`つけて区別したり。。

## 無法地帯なpublic

コンストラクタの引数にアクセス指定子を追加すると、同じ名前でメンバ変数を作ってくれます。ここを`public`にしちゃえばコードはかなりシンプルになります。

```ts:TypeScript
class Item {
  constructor(public name: string) { }
}

const item = new Item('Apple')
console.log(item.name) // Apple
item.name = 'Orange'
console.log(item.name) // Orange
```

この書き方はとても好きなのですが、setter以前のメンバ変数直代入を認めるので、使う側はもう無法地帯。。ちょっとこれで妥協はしたくないですねぇ。

# Rubyの素敵な例

Rubyには`attr_reader`ってのがあって、getterだけをシンプルに作ってくれます。

```rb:Ruby
class Item
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

item = Item.new('Apple')
puts item.name #=> Apple
item.name = 'Orange' #=> undefined method `name=' (NoMethodError)
```

Rubyはそもそもインスタンス変数にクラス外から直アクセス禁止(`Hoge.new().@fuga`みたいのは不可)なので、単純比較はできないけどこれはとっても素敵。
TypeScriptでもこんな風に書けないものか！？

# TypeScriptの素敵な例

書けました！`readonly`を使います。

```ts:TypeScript
class Item {
  constructor(readonly name: string) { }
}

const item = new Item('Apple')
console.log(item.name) // Apple
item.name = 'Orange' // Cannot assign to 'name' because it is a read-only property.
```

シンプルで安全で良いですね！

## readonlyとは？

http://www.typescriptlang.org/docs/handbook/interfaces.html#readonly-properties
この`readonly`は、どんなアクセス指定子とも組み合わせて使えます。

```ts
  public readonly hoge: string
  protected readonly fuga: string
  private readonly piyo: string
```
たとえば`private readonly`の場合は、「クラス内のメソッドでも変更不可（コンストラクタでの初期化のみ）」となります。

```ts
class Hoge
  private readonly hoge: string
  constructor(public _hoge: string) {
    this.hoge = _hoge // OK
  }
  setHoge(_hoge: string) {
     this.hoge = _hoge // Cannot assign to 'hoge' because it is a read-only property.
  }
}
```

コンストラクタの引数でも、もちろん同様に使えます。

```ts
  constructor(public readonly hoge: string) { }
  constructor(protected readonly fuga: string) { }
  constructor(private readonly piyo: string) { }
```

TSではアクセス指定子省略は`public`なので、この2つは等価ってことですね。

```ts
  constructor(public readonly hoge: string) { }
  constructor(readonly hoge: string) { }
```

今知ったけど、C#にも`readonly`がありました。同じMicrosoftだし、これがTypeScriptにも導入されたのですねきっと。
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/readonly

# おわりに
C#使いにとっては当たり前なんでしょうけど、Rubyの`attr_reader`と比較する記事は見つからなかったので、これで世界のググラビリティが少し上がったのではないかと思います！

型でキッチリ書くことと、コードが冗長にならないこと、ぜひとも両立をめざしたいものですねっ！TypeScriptでは`public readonly`を活用していきましょう :smile: 

ではまた！