🧡

TypeScriptではswitch文より辞書的な定数で定義する方が好き

に公開

概要

仕事を進める中で、TypeScriptでswitch文を用いている部分を、辞書的な定数で表現した方が良いと考える場面がありました。私はこの辞書的な定数の方が好きです。この記事では、備忘録も兼ねてその手法について説明します。

前提条件

ポケモンにおいて、特定のタイプに対して効果が抜群なタイプを取得するケースを考えます。この例では、次のTypeScriptのリテラル型を使用します。

/** ポケモンのタイプを表すリテラル型(炎、水、草、電気) */
type PokemonType = 'fire' | 'water' | 'grass' | 'electric';

switch文での表現

まず、効果抜群なタイプを取得するためのよくあるswitch文を示します。もちろん、この方法でも問題ありません。この例では、PokemonTypeに新しいタイプ(例: 'normal')を追加したとしても、type satisfies neverでLintエラーが出るため、容易に発見できます(最近読んだこの記事の内容も盛り込んでみました)。

/** 効果が抜群なタイプを取得 */
const getVeryEffectiveType = (pokemonType: PokemonType): PokemonType => {
  switch (pokemonType) {
    case 'fire':
      return 'water';
    case 'water':
      return 'grass';
    case 'grass':
      return 'fire';
    case 'electric':
      return 'water';
    default:
      throw new Error(`Unexpected type: ${pokemonType satisfies never}`);
  }
};

const effectiveType = getVeryEffectiveType('fire'); // 炎には水

辞書的な定数での表現

次に、定数を使用して特定のタイプと効果抜群なタイプの関係を辞書的な変数として定義する手法を説明します。as const satisfies型のwideningも防いでいます。そのためここでも、PokemonTypeに新しいタイプを追加するとLintエラーが出ます。また、コンパイル後のJavaScriptのサイズはswitch文より若干短縮されます。

/** 効果が抜群なタイプを宣言的に定義 */
const VeryEffectiveTypeDict = {
  fire: 'water',
  water: 'grass',
  grass: 'fire',
  electric: 'water',
} as const satisfies {
  [key in PokemonType]: PokemonType;
};

const effectiveType = VeryEffectiveTypeDict['fire']; // 炎には水

まとめ

  • 辞書的に定義しても、switch文と同様に型のエラーチェックが可能です。
  • 宣言的にコーディングできるので、可読性の良さや変更のしやすさはあります。
  • また、コンパイル後のJavaScriptのサイズが小さくなるため、大量の変数を扱う場合には有効です。
  • ただし、複雑なロジックの切り替えなど、switch文で表現する方が適しているケースもあります。
  • 私ならコーディングルールに載せる場合、可能であればそうして欲しいというニュアンスで、「should」程度に設定すると思います。

Discussion