【実務・中級編】 リテラル型(boolean, number, string) – TypeScript実践ガイド

TypeScriptの「リテラル型」を極める:静的解析を武器にするための現場的戦略

こんにちは。フロントエンドの現場でコードレビューをしていると、「なぜそこでその型を当てたのか?」と問いかけたくなるシーンによく出くわします。特に、中級者以上のエンジニアが陥りがちなのが、`string`や`number`といった「広すぎる型」に甘んじてしまうこと。

今日は、TypeScriptの静的型システムを最大限に活かし、バグを未然に防ぐための「リテラル型」について、少し深いところまで掘り下げていこう。

—

1. リテラル型とは何か?―「値そのもの」を型にする

TypeScriptにおけるリテラル型とは、`string`や`number`といった広大な集合の一部分だけを切り出した、「特定の値のみを許可する型」のことだ。

例えば、`const status = ‘active’` と書いたとき、TypeScriptは推論によって、この変数を `string` ではなく `’active’` というリテラル型として扱う。これは単なる文字列ではなく、「’active’ という値以外は一切受け付けない」という強力な境界線だ。

// string型だと、どんな文字列でも入ってしまう(バグの温床)
let statusGeneral: string = ‘active’;
statusGeneral = ‘hoge’; // 通ってしまう!

// リテラル型なら、定義外の値は即座にコンパイルエラー
let statusStrict: ‘active’ | ‘inactive’ = ‘active’;
statusStrict = ‘inactive’; // OK
// statusStrict = ‘hoge’; // Error: Type ‘”hoge”‘ is not assignable to type ‘”active” | “inactive”‘

ブラウザの裏側、つまり実行時のJavaScriptにはこの「型」は存在しない。しかし、コンパイル時にこの「型」があるおかげで、開発者は「ありえない状態」をコード上で物理的に排除できる。これがTypeScriptの真骨頂だ。

—

2. ユニオン型との組み合わせが「最強」である理由

現場で最も多用するのが、リテラル型を「ユニオン型(`|`)」で束ねるパターンだ。これは、APIのレスポンスや、コンポーネントのProps定義において「特定の選択肢しかない状態」を表現するのに最適だ。

// ボタンのサイズを制限する実用例
type ButtonSize = ‘sm’ | ‘md’ | ‘lg’;

interface ButtonProps {
label: string;
size: ButtonSize; // これにより、’xl’などの未定義サイズを入力できないようにする
}

const myButton: ButtonProps = {
label: ‘保存’,
size: ‘md’, // ここでエディタの補完が効く。DX(開発体験)が爆上がりする瞬間だ
};

このように定義しておけば、誰かが適当な文字列を入力しようとした瞬間に、VSCodeが赤線を出して警告してくれる。コードレビューで「この変数の値は何になり得るのか?」と議論する時間は、もう不要だ。

—

3. 【現場の知見】なぜ「string型」を避けるべきか

多くのエンジニアが `type Status = string` と書いてしまう。なぜこれがダメなのか? それは、「型情報が意味を成さないから」だ。

例えば、`processUser(status: string)` という関数があったとしよう。これだと、呼び出し元が ` ‘active’` を送るべきなのか、`’pending’` を送るべきなのか、あるいは全く別の文字列なのか、コードを読むまで誰も確信できない。

リテラル型とユニオン型を使えば、関数のシグネチャが「ドキュメント」になる。

type UserStatus = ‘pending’ | ‘active’ | ‘suspended’;

/

  • ユーザーの状態を更新する関数
  • 型定義だけで、何が有効な状態かが一目でわかる

/
function updateUserStatus(status: UserStatus): void {
console.log(`ステータスを ${status} に更新します`);
}

// 呼び出し側も明確。間違った値は渡せない
updateUserStatus(‘active’);

—

4. 応用編:`as const` でオブジェクトを「定数化」する

実務では、配列やオブジェクトをリテラル型として扱いたい場面が多々あるはずだ。そのとき、`as const`(Const Assertion)を使わない手はない。

// as const をつけることで、プロパティを読み取り専用のリテラル型として推論させる
const COLORS = {
PRIMARY: ‘#007bff’,
SECONDARY: ‘#6c757d’,
DANGER: ‘#dc3545’,
} as const;

// COLORS.PRIMARY は string ではなく ‘#007bff’ というリテラル型になる
type ColorValue = typeof COLORS[keyof typeof COLORS];

function setBorderColor(color: ColorValue) {
// …
}

setBorderColor(COLORS.PRIMARY); // OK
// setBorderColor(‘#000000’); // Error: 型が一致しない

このテクニックを知っているだけで、型定義のメンテナンスコストが劇的に下がる。`COLORS` オブジェクトを修正するだけで、関連する型も自動的に追従してくれるからだ。

—

まとめ:型は「守り」ではなく「攻め」の武器だ

リテラル型を使いこなすことは、単にバグを減らすことではない。「自分の書いたコードが、他の誰にとっても明確に振る舞うように設計する」という、エンジニアとしての品格に関わることだ。

「とりあえず `string` でいいか」という妥協は、将来の自分に対する負債になる。今日紹介したリテラル型とユニオン型の組み合わせを、まずは明日のプルリクエストから意識して取り入れてみてほしい。エディタの補完が味方してくれる感覚、きっとクセになるはずだ。

何か詰まったら、いつでも相談してくれ。現場からは以上だ。

コメント

タイトルとURLをコピーしました