TypeScript：constructorの引数でクラス変数の定義を省略する方法

はいさい！ちゅらデータぬオースティンやいびーん！

# 概要

TypeScriptで`class`構文を書く時に、`constructor`関数の引数に`public`、`protected`および`private`の接頭辞を使うと、同時にインスタンス変数の定義ができるので、その手法を紹介します。

## 背景

[Angular](https://angular.io/)で[Serviceの説明](https://angular.io/guide/architecture-services)を読んでいたのですが、不思議な書き方を見て気になりました。

```typescript
export class HeroService {
  private heroes: Hero[] = [];

  constructor(
    private backend: BackendService, // これです
    private logger: Logger) { }

  getHeroes() {
    this.backend　// しかもこうやって定義していないはずのクラス変数を使っているし
      .getAll(Hero).then( (heroes: Hero[]) => {
      this.logger.log(`Fetched ${heroes.length} heroes.`);
      this.heroes.push(...heroes); // fill cache
    });
    return this.heroes;
  }
}
```

どういうことだろうと思い、TypeScriptのドキュメントを調べました。

その結果を本記事で備忘録代わりにまとめます。

# コンストラクター割り当て(Constructor Assignment）

このTypeScriptの構文を`Constructor Assignment`と言っています。

TypeScriptならではの速記手法です。

例えば通常、こう書くでしょう。

```typescript
class MyObject {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

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

const obj = new MyObject("austin");
console.log(obj.getName()); // "austin"
```

https://www.typescriptlang.org/play?#code/MYGwhgzhAECyCeB5ARgKwKbAC7QN4ChojoAHAJwEsA3MLdaAOzAFt0AuaCLShgcwG58hYsAD2DLmQCu2UWQAUTVh0kU+ASjzDixLAAsKEAHRL6AXkYt0gnQF8hOklOQgKwaL3RYAclfnqVbjVeLR0dMi8pMgZofUMTKxtie3t8MQkcUTRoCwZ0AHc4JDRMLHkAIjApLjVy9UF0iFEQdCMQUV55LNQjTx8-dXqgA

しかし、同じことが以下のような省略記述で表現できます。

```typescript
class MyObject {
    constructor(private name: string) {}

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

const obj = new MyObject("austin");
console.log(obj.getName());
```

https://www.typescriptlang.org/play?#code/MYGwhgzhAECyCeB5ARgKwKbAC7QN4ChojpgB7AOwiwCcBXbU6gCgAdqBLANzC3WnLABbdAC5oVDuQDmASjwBffIWItayEO2DQp6LADkh6JjLET20vMuLFqu2tXLQsAC3YQAdAOEBuK9EWK+GSUOKRo0AC8-OgA7nBIaJhYTABEYLRU5ikyvsEQpCDo7iCkUkxhqO46+obGOUA

かなりすっきりしますね。

ぱっと見てAngularの魔法に見えたのが実はちゃんとしたTypeScriptだったので安心しました。

**Angularはちゃんとルールを守る優等生**。

# publicも、protectedも同様に使える

`private`のみならず、`public`と`protected`で同様な記述法ができます。

### public

```typescript
class MyObject {
    constructor(public name: string) {}
}

const obj = new MyObject("austin");
console.log(obj.name);
```

### protected

```typescript
class MyObject {
    constructor(protected name: string) {}
}

class ExtendedObject extends MyObject {
    get objName(): string {
        return this.name;
    }
}

const obj = new ExtendedObject("austin");
console.log(obj.objName);
console.log(obj.name); // TS Error 
```

https://www.typescriptlang.org/play?#code/MYGwhgzhAECyCeB5ARgKwKbAC7QN4ChojpgB7AOwiwCcBXbU6gCgAdrStNOATacsALboAXNCrUAluQDmASjwBffEvyhIMAKIAPTuW7puKDNmjod6PTARGueQsWnocpNADlB6JrNHip0u8SB0NROtNTk0FgAFhIQAHT8QgDc9kRKKmSUzmjQALx86ADu0Nq6+oZoXEwARGC0VFLVsimZEKQg6HEgpNJMLqhx-e5CzUA

# まとめ

以上、クラス構文の`constructor`メソッドでクラス・インスタンス変数を定義する速記法を紹介しました。

使うかどうかはいいとして、見た時に、「こういうことをやっているのだな」とわかればいいのかなと思います。

冗長なコードは必ずしも悪いコードではありません。

**見てすぐにわかるようなコードが何よりもいいコードである**のです。

なので、筆者としてはこの手記法を推奨するというよりは、プロジェクトによって**最もわかりやすい書き方を優先するべきだ**と考えています。

脱線ですが、パフォーマンスにこだわって読みやすさを犠牲にするスタイルは世の中にあります。いわゆる _パフォーマンス至上主義_ という考え方です。

筆者は真っ向から**その考え方は間違っている**と思います。

例えループが一つ増えたとしても、それによってわかりやすく書けるのであれば、ループを一つ増やしましょう。

_（但し、ループの中のループ、いわゆる`nested loop`は避けましょう！）_

数ヶ月後の、そのコードを書いた自分が、感謝してくれますよ。数ヶ月後の自分を思いやりましょう。