TypeScript/網羅性を考慮した分岐処理

2 min read読了の目安(約2100字 1
if (status === 1) {
} else if (status === 2) {
} else if (status === 3) {
} else if (status === 4) {
}

この記述は、以下の点で望ましくない。

  • マジックナンバーである
    • たとえば 1 にどのような意味があるのかわからない
  • リテラルに依存している
    • リテラルとの対応関係に変更があると、ソースコードの変更は依存箇所すべてに及ぶ

...

if (status === "FOO") {
} else if (status === "BAR") {
} else if (status === "BAZ") {
} else if (status === "QUX") {
}

マジックナンバーの代わりに文字列で比較できるならば、
status の意味が読み手に伝わりやすいため、より望ましいように思える。

ただし、たとえば status が外部から与えられた数値であったときは、
マジックナンバーから読み手に伝わりやすい文字列に、変換する処理が必要になってしまう。

...

if (status.isFoo()) {
} else if (status.isBar()) {
} else if (status.isBaz()) {
} else if (status.isQux()) {
}

上記のようにも書くことができる。
これは先掲の望ましくない点を克服しているが、新たに以下の点で望ましくない。

  • state が取りうる値の網羅性について検証できない

...

const externalAnyParam = {
  1: "FOO",
  2: "BAR",
  3: "BAZ",
  4: "QUX",
} as const;

const exampleOne = (arg: keyof typeof externalAnyParam): string => {
  const value = externalAnyParam[arg];
  if (value === "FOO") {
    return "foo";
  } else if (value === "BAR") {
    return "bar";
  } else if (value === "BAZ") {
    return "baz";
  } else if (value === "QUX") {
    return "qux";
  } else {
    // eslint-disable-next-line no-case-declarations,@typescript-eslint/no-unused-vars
    const _exhaustiveCheck: never = value;
  }
};

// もちろん switch で書いても検証を行うことができる
const exampleTwo = (arg: keyof typeof externalAnyParam): string => {
  const value = externalAnyParam[arg];
  switch (value) {
    case "FOO":
      return "foo";
    case "BAR":
      return "bar";
    case "BAZ":
      return "baz";
    case "QUX":
      return "qux";
    default:
      // eslint-disable-next-line no-case-declarations,@typescript-eslint/no-unused-vars
      const _exhaustiveCheck: never = value;
  }
};

ゴリ押し感があるが、この記述はすべての望ましくない点を克服している。
(ためしに分岐のどれかを削除すると静的解析のエラーになる)
変換表があるおかげで、腐敗防止層にもなっている。

が、逐一変換表を呼び出すのはなんかイケてなく、他にやりかたがありそうに思える。
(ひとによっては呼び出すのをめんどくさがってマジックナンバーで比較しそう)

...

もちろん OOP で手厚い実装を行えば、可読性と独立性と網羅性をすべて上々にできるであろう。
しかし、分岐処理を行うために、そこまでの実装を行うのは過剰であるようにも思える。

...

(リンク)

https://typescript-jp.gitbook.io/deep-dive/type-system/discriminated-unions