【TypeScript】Mapped typeを活用して拡張性の高いコードを書く


はじめに

TypeScript+Reactなどの環境で、コンポーネントのタイプによって背景色や文字色などを切り替えたいとき、Mapped typeを使うと拡張性のある実装を行うことができるよ!という話です。
こちらTypeScript初心者向けの記事になりますので、もっと基礎向けの内容も知りたいという方は以下の記事もご覧ください。

実装

背景色や文字色を"WARNING"と"DANGER"の2パターン用意したいとき、三項演算子を使えば以下のように書くことができます。

const backgroundColor = type === "WARING" ? "yellow" : "red";
const color = type === "WARING" ? "white" : "black";

しかし、今後"NORMAL"というパターンも用意しなければいけなくなったとき、三項演算子を使うことはできなくなります。
機能拡張のために、ifswitchを使った実装変更を行う必要がでてきてしまいます。

パターンを1つ増やすだけでこのような実装変更を行う必要があるのは、決して拡張性の高いコードとはいえません。
このときに利用するのがMapped typeです。

type AttentionType = "WARNING" | "DANGER";

type AttentionTheme = {
  backgroundColor: string;
  color: string;
};

const AttentionThemeMap: { [key in AttentionType]: AttentionTheme } = {
  WARNING: {
    backgroundColor: "yellow",
    color: "white",
  },
  DANGER: {
    backgroundColor: "red",
    color: "black",
  },
};

AttentionTypeで必要なパターンをユニオンリテラル型で定義し、AttentionThemeで各パターンにおけるオブジェクトのプロパティの型を定義します。
すると、AttentionThemeMap: { [key in AttentionType]: AttentionTheme }のようなMapped typeを定義することができます。

各パターンにおけるbackgroundColorcolorを以下のように取得することができるのですが、typeAttentionType("WARNING"と"DANGER")以外だと、TypeScriptが赤波線でエラーを出して注意してくれます。

const { backgroundColor, color } = AttentionThemeMap[type]

また、"NORMAL"のような新しいパターンを追加したいとき、必要なのはAttentionTypeAttentionThemeMapへの簡単なコード追加のみとなります。

type AttentionType = "WARNING" | "DANGER" | "NORMAL";

const AttentionThemeMap: { [key in AttentionType]: AttentionTheme } = {
  WARNING: {
    backgroundColor: "yellow",
    color: "white",
  },
  DANGER: {
    backgroundColor: "red",
    color: "black",
  },
  NORMAL: {
    backgroundColor: "green",
    color: "white",
  },
};

条件変更をifswitch頼みにすると、コードの可読性も下がってしまいます。
明日からMapped typeを使ってみましょう!

参考資料