⚡️

TypeScriptで設定しておきたいオプションを逆引きで紹介する

に公開

タイトルの通りです。

この記事ではTypeScriptでプロジェクトを開始する際に自分が基本的に有効にするオプションを逆引きで紹介します。

暗黙のanyを禁止する

strictが有効であれば必然的に有効。個別に設定する場合はnoImplicitAnyで設定が可能。

例えば以下のように引数の型注釈を省略した場合anyと評価されますが、それを禁止します。

非null型や非undefined型の変数にそれらの代入を禁止する

strictが有効であれば必然的に有効。個別に設定する場合はstrictNullChecksで設定が可能。

例えば非string型にも関わらず、nullを代入するとエラーとなります。strictNullChecksfalseであれば、エラーとなりません。

undefinedでも同様です。

引数の共変性を禁止する

strictが有効であれば必然的に有効。個別に設定する場合はstrictFunctionTypesで設定が可能。

引数の共変性とは

例えば以下のような型エイリアスがあるとします。

type getNumber = (num: number | null) => number;

この型エイリアスに対して、TypeScriptとしては以下のように代入することはできるでしょうか?

type getNumber = (num: number | null) => number;

const getNumber: getNumber = (num: number) => {
  return num*num;
};

答えは、「可能」です。

では、この状況では何が問題でしょうか?

そう、型エイリアスがnumberにも関わらず、nullを渡すことができてしまっています。

これにより、null同士の乗数が発生してしまいます。

このように、型定義の引数型より範囲を狭められる性質のことを引数の共変性といいます。

ただしこれはstrictFunctionTypesが無効になっている場合です。有効にしている場合、エラーを出してくれます。

一方で、型定義の引数型より範囲を広められる性質のことを引数の反変性といいます。

反変性の例
type getNumber = (num: number) => number;

const getNumber: getNumber = (num: number | null) => {
  return num || 0;
};

反変の場合は、実装に対して指定した型への処理が不足することはないため、TypeScriptでは許容されています。

クラスプロパティの初期化を必須にする

strictが有効であれば必然的に有効。個別に設定する場合はstrictPropertyInitializationで設定が可能です。

解消のためには普通に初期化、コンストラクタで初期化、undefinedのユニオン型で明示的に型注釈するの3つの方法があります。

class Car {
  carNumber: number;
  carName: string | undefined; // undefined
  carModel: string = "TOYOTA"; // 初期化子で初期化

  constructor(carNumber: number) {
    this.carNumber = carNumber; // コンストラクタで初期化
  }
}

未使用の変数がある場合に怒ってもらう

noUnusedLocalsで設定が可能。未使用の変数などゴミでしかないので、必ず有効にしておきたいオプションです。

未使用の引数がある場合に怒ってもらう

noUnusedParametersで設定が可能。noUnusedLocalsと同じく、必ず有効にしておきたいオプションです。

空でないcase文でbreakやreturnを設定するよう怒ってもらう

noFallthroughCasesInSwitchで設定が可能。

例えば以下のログレベルに応じた出力の切り替え関数があるとします。

type LogLevel = 'info' | 'warn' | 'error'
const switchLog = (log: string, level: LogLevel) => {
  switch (level) {
    case 'info':
      console.log(log)
    case 'warn':
      console.warn(log)
    case 'error':
      console.error(log)
    default:
      throw new Error('Invalid log level')
  }
}

switchLog('info log', 'info')

この場合、noFallthroughCasesInSwitchが有効な場合、実装時にエラーとはなりません。

ですが当然このコードはうまく動きません。

info log
info log
info log
/src/playground.js:10
            throw new Error('Invalid log level');

このあるあるなバグを回避するためには、noFallthroughCasesInSwitchを有効にしておくと良いです。

これにより、空でないcase文でのbreakやreturnがある場合に怒ってくれるようになります。

おわりに

個人的に自分が必ず有効にするオプションを逆引きでご紹介しました。といいつつ、最新のTypeScript環境を作ればstrictが有効になっているため、基本的にここで挙げたオプションは有効になっているはずです。

そのほか「これも基本有効にしてていいよ!」というものがありましたら、ぜひコメントでお教えください!

📢 Kobe.tsというTypeScriptコミュニティを主催しています

フロント・バックエンドに限らず、周辺知識も含めてTypeScriptの勉強会を主催しています。

毎朝オフラインでもくもくしたり、神戸を中心に関西でLTもしています。

盛り上がってる感を出していきたいので、良ければメンバーにだけでもなってください😣

https://kobets.connpass.com/

Discussion