【入門編】TypeScript 5.xにおける「const型パラメータ」を用いた関数引数の型推論の最適化 – TypeScript コア・型システムの基礎解析バイブル

こんにちは!TypeScriptの型システムの世界へようこそ。
フルスタックチーフアーキテクトの私です。

今回は、TypeScript 5.xで導入された強力な武器、「const型パラメータ(Const Type Parameters)」についてお話ししますね。

「関数の引数に渡したオブジェクトや配列の型が、いつの間にかただの `string` や `number` に抽象化されてしまって困った……」
そんなモヤモヤを感じたことはありませんか?

ここをクリアすれば、TypeScriptの型推論の解像度が劇的に上がり、あなたのコードはコンパイル時に「完全な安全性」を手に入れることができます。一緒にマスターしていきましょう!

—

1. なぜ「const型パラメータ」が必要なのか?(従来の課題)

まずは、TypeScriptが普段どのように型を推論しているかを見てみましょう。
例えば、次のような「設定オブジェクト」を受け取る関数があるとします。

function configure(config: { endpoint: string; retries: number }) {
// 何らかの処理
}

// 呼び出し
configure({
endpoint: “/api/v1/users”,
retries: 3,
});

このコード、一見すると何の問題もありませんよね。
しかし、型推論の裏側を覗いてみると、TypeScriptは次のように解釈しています。

  • `endpoint` は `string` 型である(特定の文字列 `”/api/v1/users”` ではない)
  • `retries` は `number` 型である(具体的な数値 `3` ではない)

もし、あなたが「この関数には、引数で渡された特定の値そのものを型として固定したい(リテラル型として扱いたい)」と思った場合、従来のTypeScriptでは次のような工夫が必要でした。

// 1. as const を使う(呼び出し側が毎回書く必要があり、面倒で忘れやすい)
configure({
endpoint: “/api/v1/users”,
retries: 3,
} as const);

// または 2. ジェネリクスと readonly を駆使する複雑な型定義をする

毎回 `as const` をつけるのは開発者体験(DX)としてあまり良くありませんし、何より書き忘れるバグの温床になりますよね。

—

2. TypeScript 5.xの救世主:`const` 型パラメータの使い方

そこで登場するのが、TypeScript 5.xで追加された `const` 型パラメータです。

使い方はとてもシンプル。ジェネリクスの型引数の手前に `const` をつけるだけです。

// 型パラメータ T の前に 「const」 を置く!
function configure(config: T) {
return config;
}

const settings = configure({
endpoint: “/api/v1/users”,
retries: 3,
});

このコードの何がすごいか分かりますか?
コンパイラが裏側でどう型を評価しているか、結果を見てみましょう。

実行時(型評価)のイメージ図

【従来の推論】
引数: { endpoint: “/api/v1/users”, retries: 3 }
┗ 🧠 TypeScript: 「ふむ、文字列と数値だね」
┗ 📝 推論結果: { endpoint: string; retries: number }

【const型パラメータの推論】
引数: { endpoint: “/api/v1/users”, retries: 3 }
┗ 🧠 TypeScript: 「おっ、constがついているな! 値をそのまま型にしよう!」
┗ 📝 推論結果: { readonly endpoint: “/api/v1/users”; readonly retries: 3 }

そうなんです!呼び出し側に `as const` を書かなくても、自動的に「読み取り専用(readonly)の厳密なリテラル型」として推論してくれるようになります。これが `const` 型パラメータの本質です。

—

3. 実践!よりリアルなコードで体感しよう

もう少し実務に近い例を見てみましょう。
例えば、ルーティングのパスや、許可されたアクション名を定義する関数の場合です。

// アクションのリストを受け取る関数
function registerActions(actions: T) {
return actions;
}

// 呼び出し
const myActions = registerActions([“create”, “read”, “update”, “delete”]);

この時、`myActions` の型は単なる `string[]` ではなく、次のように推論されます。

// 実際の推論結果
readonly [“create”, “read”, “update”, “delete”]

これにより、次のようなメリットが生まれます。

1. 配列の要素の順番や値が型として完全に保護される
2. 存在しないアクションを指定した際に、コンパイルエラーで即座に検知できる

// 例: 型の安全性による恩恵
type AllowedAction = typeof myActions[number];
// 結果: “create” | “read” | “update” | “delete” のユニオン型になる!

わざわざ手動で `type AllowedAction = “create” | …` と書き直す必要がなくなるわけです。型定義の重複(DRY原則の違反)を防ぐ上でも、このテクニックは非常に強力です。

—

4. 陥りやすい文法エラーと注意点

ここで、初心者の開発者の方がよくハマりがちなポイントをいくつか解説しておきますね。

① `const` を置く位置を間違える

`const` は関数名や引数ではなく、型パラメータ(ジェネリクス)の定義部分に置く必要があります。

// ❌ 誤った例:引数の前につけようとしてしまう
function badFunc(const config: { name: string }) {} // SyntaxError!

// ❌ 誤った例:型パラメータの変数名自体に混ぜようとする
function badFunc(config: T) {} // SyntaxError!

// ✅ 正しい例:型パラメータの宣言の先頭
function goodFunc(config: T) {}

② すべての関数に `const` をつければいいわけではない

「じゃあ、すべての関数のジェネリクスに `const` をつければ最強じゃん!」と思われがちですが、それは少し違います。

  • 値の変更(ミューテーション)が必要なデータを扱う場合
  • 呼び出し側が「一般的な型(`string`など)」として渡したいと意図している場合

これらに `const` 型パラメータを強制すると、型がガチガチになりすぎて逆に柔軟性を失うことがあります。「リテラル型として正確に固定したいデータ(設定、設定ファイル、ルート定義など)」に絞って使うのが、プロのアーキテクトとしての選択です。

—

まとめ:ここをクリアすればTypeScriptの基本はバッチリ!

今回は、TypeScript 5.xにおける「const型パラメータ」について解説しました。

  • 何をするもの?: 関数の引数のリテラル型や構造を、`as const` なしで自動的に厳密(readonly)に推論させる仕組み。
  • どう書く?: ジェネリクスの構文で `` と指定する。
  • どんな時に使う?: 設定オブジェクトや定数配列など、値そのものを型として利用したいとき。

この構文を自然に使いこなせるようになると、TypeScriptの型推論が「自分の意図通りに賢く動く」ようになり、コーディングが一段と楽しくなりますよ。

日々の開発の中で「あ、ここは値の型を落としたくないな」と思ったときは、ぜひこの `const` 型パラメータを思い出してくださいね。それでは、次のステップへ進みましょう!

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