【入門編】TypeScript 5.xにおけるconst型パラメータと配列の型推論の進化 – TypeScript コア・型システムの基礎解析バイブル

こんにちは!フロントエンドからバックエンドまで、TypeScriptの荒波を一緒に航海していく先輩エンジニアです。

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

「配列やオブジェクトを定義したのに、なんだか型が広く推論されてしまって困る…」
「`as const`を毎回書くの、ちょっと面倒だな…」

そんなモヤモヤを抱えていませんか?ここをクリアすれば、あなたのTypeScriptの型推論に対する理解は一段と深まり、コードの安全性と書き心地が劇的に向上しますよ。さあ、一緒に本質をマスターしていきましょう!

—

1. 従来の型推論の「もどかしさ」を知る

まずは、TypeScript 5.x以前の私たちが直面していた「ちょっと不都合な真実」からおさらいしましょう。

例えば、受け取った配列をそのまま返すだけのシンプルな関数を作ったとします。

function identity(arg: T): T {
return arg;
}

// 文字列の配列を渡してみます
const languages = identity([“TypeScript”, “Rust”, “Go”]);
// 推論される型: string[] (あれ?もっとカチッとさせたいのに…)

私たちは「`[“TypeScript”, “Rust”, “Go”]` という具体的なリテラル型の配列だ!」と思って渡したはずなのに、コンパイラは `string[]` という広い型(Widenされた型)に一般化して解釈してしまいます。

これを防ぐために、これまで私たちは以下のように書いていました。

// as const を使って、これ以上型を広げないで!と指示する
const languages = identity([“TypeScript”, “Rust”, “Go”] as const);

もちろん `as const` は今でも強力ですが、関数を呼び出す側(ユーザー)に毎回 `as const` を強いるのは、ライブラリ設計などの場面で少しデベロッパーエクスペリエンス(DX)が下がってしまいますよね。「関数を呼ぶだけで、自動的に一番狭い型(`const`な型)で推論してくれたら最高なのに……!」

その願いを叶えるために登場したのが、`const`型パラメータです。

—

2. TypeScript 5.xの救世主:`const`型パラメータとは?

TypeScript 5.0で導入された `const` 型パラメータを使うと、「関数に渡された引数の型を、可能な限りイミュータブルかつリテラルな型として推論する」ようにコンパイラにお願いできます。

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

// 型引数 T の前に const をつける!
function createReadonlyList(items: T): T {
return items;
}

const frameworks = createReadonlyList([“React”, “Vue”, “Svelte”]);
// 🌟 TypeScript 5.xでの推論結果:
// readonly [“React”, “Vue”, “Svelte”]

見てください! `as const` を外側につけなくても、関数に渡すだけで、タプル型かつ `readonly` な厳密な型として推論されました。

図解:型が広がる世界 vs 狭まる世界

【従来の推論 (Widen)】
[“React”, “Vue”] ──> string[] (なんでも入る配列になってしまう)

【const型パラメータによる推論 (Narrow)】
[“React”, “Vue”] ──> readonly [“React”, “Vue”] (この要素・この順序で確定!)

この「型がカチッと決まる」感覚、たまりませんよね。

—

3. 実践!設定オブジェクトを安全に受け取る関数を書く

もう少し実用的な例を見てみましょう。ルーティングの設定や、APIのエンドポイント一覧などを扱う場面を想像してください。

// エンドポイントの設定をそのまま厳密な型として受け取りたい
function registerRoutes(routes: T) {
return {
routes,
count: routes.length,
};
}

const appRoutes = registerRoutes([“/api/users”, “/api/posts”, “/api/settings”]);

// appRoutes.routes は以下の型を完全に保持しています:
// readonly [“/api/users”, “/api/posts”, “/api/settings”]

もしここで、存在しないルートを指定してしまったり、誤って値を書き換えようとしたりすると、TypeScriptのコンパイラが即座にエラーを教えてくれます。

// ❌ コンパイルエラー!
// 理由: 読み取り専用(readonly)のタプルなので、要素の代入・変更はできません
appRoutes.routes[0] = “/api/v2/users”;

実行時エラーの温床になりやすい「配列の意図しない書き換え」を、コンパイル時に完全にシャットアウトできるのです。

—

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

非常に便利な `const` 型パラメータですが、使い所を間違えるとコンパイルエラーや予期せぬ挙動に出くわします。ここでよくある落とし穴を確認しておきましょう。

落とし穴1: `const` をつける場所を間違える

`const` は型パラメータ(ジェネリクス)の定義部分につけます。引数の前につけてもエラーになります。

// ❌ 誤った書き方(構文エラーになります)
function badFunction(const items: T) {}

// ⭕ 正しい書き方
function goodFunction(items: T) {}

落とし穴2: 可変(Mutable)に扱いたい変数への適用

`const` 型パラメータを使うと、推論結果は基本的に `readonly`(読み取り専用)になります。もし後から要素を `push` や `pop` するような配列を扱いたい場合は、この機能は適していません。

あくまで「この設定値やデータ構造は、もう変わらないものとして厳密に扱いたい!」というイミュータブルなデータ設計のときに真価を発揮します。

—

まとめ

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

  • 従来の悩み: 配列やオブジェクトを渡すと、型が `string[]` や `object` などに広く(Widen)推論されてしまっていた。
  • 解決策: ジェネリクスに `` と指定することで、関数呼び出し側で `as const` を書かなくても、リテラルかつ `readonly` な精度の高い型推論を得られるようになった。
  • メリット: コードの安全性が高まり、ライブラリやユーティリティ関数のDX(開発者体験)が劇的に向上する。

ここをクリアできれば、TypeScriptの型推論の仕組みをかなり深くコントロールできるようになります。型が自分の意図通りにピタッと決まる気持ちよさを、ぜひ実際の開発でも味わってみてくださいね。

あなたのTypeScriptライフが、よりエラーレスで楽しいものになりますように。それではまた!

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