TL;DR
- TypeScript 5.x の型システム進化は「新文法を覚える話」ではなく「型を厚くする場所を選ぶ話」です。 satisfies 演算子・const type parameters・
using宣言・新 decorators の4つは、いずれも「推論を殺さずに制約を足す」方向に効きます(TypeScript 5.0 リリースノート / 確認日 2026-06-06) - 最も実務インパクトが大きいのは
satisfies。 「この値はこの型に適合すること」を保証しつつ、リテラルの絞り込んだ推論を保持できます。設定オブジェクトやマッピングテーブルで型注釈: Tを機械的に外せます consttype parameters は、ジェネリック関数に渡したリテラルがstringに広がる事故を引数側で止めます。ライブラリ作者だけでなく、社内ユーティリティを書くチームにも効きますusing宣言(TC39 Explicit Resource Management) で、DB接続・ファイルハンドル・スパンなどのリソース解放がtry/finallyなしで書けます。TypeScript 5.2 以降の対応です- 過剰な型付けは負債になります。 「型パズルが通った」ことと「型が壊れたら気づける」ことは別物です。本記事は4機能の使いどころと、止めどころの判断軸を提示します
はじめに
こんにちは、みねです。
「TypeScript 5 で satisfies が入ったのは知っているが、: T の注釈と何が違うのか説明できない」「const type parameters を入れた PR が来たが、レビューで止めるべきか判断できない」── チームの Tech Lead からよく受ける相談です。
結論から言うと、TypeScript 5.x の型システム進化は「文法の追加」ではなく「型推論を壊さずに制約を足す手段の追加」 です。この視点を持たないと、新機能を「とりあえず使う」か「よく分からないから使わない」の二択に陥り、どちらも設計判断にはなりません。
本記事は、TypeScript 5.x(5.0〜5.4 系、確認日 2026-06-06)を本番運用しているチーム向けに、satisfies / const type parameters / using / 新 decorators の4機能を「いつ使い、どこで止めるか」 の判断軸で整理します。前提は TypeScript 5.2 以降。tsconfig の target は ES2022 以上を想定します。
型と実装の境界をどう引くかという話は、フロントエンド全体の設計にも直結します。レンダリング境界の設計は RSCとClient境界の引き方、性能改善の優先順位は フロント性能改善の正しい順序: LCP→INP→CLS も併せて参照してください。
§1. satisfies:推論を殺さずに「適合」を保証する
TypeScript 5.0 で正式化された satisfies 演算子が、5.x で最も実務に効く機能です(TypeScript 5.0 リリースノート)。
従来、設定オブジェクトに型を付けるには : T 注釈を使っていました。しかしこれは 推論を広げてしまう という副作用があります。次の例を見てください。
type RouteConfig = Record<string, { path: string; auth: boolean }>;
// 従来:型注釈 :T を付けると、各キーの具体性が失われる
const routesA: RouteConfig = {
home: { path: "/", auth: false },
admin: { path: "/admin", auth: true },
};
// routesA.home は推論されるが、キー "home" / "admin" の存在は型から消える
// routesA.dashboard でもエラーにならない(Record の index signature のため)
// satisfies:適合チェックはするが、推論は具体値を保持する
const routesB = {
home: { path: "/", auth: false },
admin: { path: "/admin", auth: true },
} satisfies RouteConfig;
// routesB.home はOK、routesB.dashboard はコンパイルエラー(キーが存在しない)
// auth は boolean ではなく false / true のリテラルとして推論される
satisfies の本質は 「型チェックは T で、推論は右辺の値で」 という分離です。設定・マッピングテーブル・色やサイズのトークン定義のように「具体的なキー・値を後段で使いたいが、形は守りたい」場面で、: T を機械的に置き換えられます。
実務での効きどころ(経験則):
- 設定オブジェクト:
tsconfig的な設定、ルーティング表、フィーチャーフラグ as constとの併用:} as const satisfies Tで readonly かつ適合保証- API レスポンスのモック: 実型に適合しつつ、テストで具体値を参照したいとき
逆に、関数の引数や戻り値の型には satisfies ではなく通常の型注釈を使います。satisfies は式の位置でしか使えず、境界の契約(公開 API のシグネチャ)はあくまで明示注釈が読みやすいためです。
§2. const type parameters:リテラルの広がりを引数側で止める
TypeScript 5.0 で導入された const type parameters は、ジェネリック関数に渡したリテラルが意図せず string や number[] に広がる事故を、呼び出し側に as const を強制せずに 防ぎます(TypeScript 5.0 リリースノート)。
// 従来:呼び出し側が as const を付けないと T が string[] に広がる
function pick<T extends readonly string[]>(keys: T): T {
return keys;
}
const a = pick(["id", "name"]); // T = string[]("id" | "name" が消える)
// const type parameter:定義側で const を付けると、リテラルが保持される
function pickConst<const T extends readonly string[]>(keys: T): T {
return keys;
}
const b = pickConst(["id", "name"]); // T = readonly ["id", "name"]
これが効くのは 社内ユーティリティやデザインシステムのヘルパー です。createTheme、defineColumns、buildQuery のような「渡したリテラルをそのまま型レベルで使いたい」関数で、利用者に as const を要求せずに済みます。利用者側の認知負荷を下げるのが価値です。
止めどころ: すべてのジェネリックに const を付けるのは過剰 です。リテラルの具体性を後段で使わない関数(単に値を変換して返すだけ)に付けると、推論結果が無駄に narrow になり、かえって型エラーを誘発します。「渡したリテラルをキーや判別子として後で使うか」を基準にしてください。
§3. using 宣言:リソース解放を try/finally から解放する
TypeScript 5.2 で TC39 の Explicit Resource Management(TC39 proposal)に対応し、using / await using 宣言が使えるようになりました(TypeScript 5.2 リリースノート)。
// Symbol.dispose を実装したリソースは、スコープを抜けると自動解放される
function getConnection(): Disposable & { query(sql: string): unknown } {
const conn = openConnection();
return {
query: (sql) => conn.run(sql),
[Symbol.dispose]: () => conn.close(),
};
}
function run() {
using db = getConnection();
db.query("SELECT 1");
// ブロックを抜けると db[Symbol.dispose]() が自動で呼ばれる
} // ← ここで close() が走る。try/finally 不要
非同期解放には await using と Symbol.asyncDispose を使います。実務での効きどころは、DB接続・ファイルハンドル・テストのモックリセット・トレーシングのスパン終了など、「確実に閉じたいが try/finally のネストが深くなる」 ケースです。target が ES2022 以下でも、polyfill(Symbol.dispose の定義)があれば動作します。
注意点: using は ブロックスコープ単位 です。関数全体ではなくブロックを抜けた瞬間に解放されるため、解放タイミングを意識した配置が必要です。
§4. 新 decorators:標準準拠への移行
TypeScript 5.0 は、TC39 Stage 3 の標準 decorators に対応しました(TypeScript 5.0 リリースノート)。従来の experimentalDecorators フラグ下のレガシー decorators とは 別仕様 で、メタデータの扱いやシグネチャが異なります。
実務での判断:
- 新規プロジェクト: 標準 decorators を選ぶ。
experimentalDecoratorsは付けない - 既存(Angular / NestJS / TypeORM 等): これらのフレームワークはレガシー decorators 前提のことが多く、
experimentalDecorators: trueを維持する。フレームワークの対応状況を確認してから移行する - 自前で decorator を書くチーム: 標準仕様の
ClassMethodDecoratorContextなどの型を使い、ロギングやメモ化のような 横断的関心事 に限定する
decorators はパワフルですが、マジックが増えてコードの追跡性が下がる 諸刃の剣です。「DI フレームワークの作法に従う」以外の用途では、まず素の高階関数で書けないかを検討してください。
§5. 過剰な型付けの罠
ここまで4機能を紹介しましたが、最も重要なのは 「型を厚くする場所を選ぶ」 ことです。TypeScript の型システムはチューリング完全に近く、複雑な条件型・テンプレートリテラル型・再帰型で「何でも表現できて」しまいます。しかしこれは罠です。
過剰な型付けの典型症状(経験則):
- 型エラーの原因が読めない: 5階層ネストした条件型のエラーメッセージは、人間には解読不能になる
- コンパイルが遅くなる: 重い型推論は
tscの型チェック時間を押し上げる。TypeScript 公式の Performance ガイド は、型の複雑さがビルド時間に直結すると明記している - リファクタ耐性が下がる: 凝った型に依存したコードは、わずかな変更で型パズル全体が崩れる
判断軸はシンプルです。「この型が間違っていたら、実行時に壊れるか?」 を問うてください。壊れる境界(外部入力のパース、API の契約、状態遷移)には型を厚く張る価値があります。一方、内部の補助関数や、テストで十分カバーできる箇所に凝った型を入れるのは、多くの場合オーバーエンジニアリングです。型は MDN の TypeScript 解説(参考)が言うように、あくまで開発時の安全装置であって、目的ではありません。
「型パズルが通った」喜びと「型が壊れたら気づける」価値を、混同しないことが肝心です。
§6. 移行の実務手順
既存プロジェクトへの導入は、影響範囲の小さい順に進めます。
- TypeScript を 5.2 以降へ更新:
pnpm add -D typescript@latestし、tsc --noEmitで既存コードの破壊的変更を確認する satisfiesから導入: 設定オブジェクトの: Tを} satisfies Tに置き換える。挙動が変わらない範囲から始めるconsttype parameters はユーティリティ層に限定: 公開ヘルパーの推論改善が見込める箇所だけに付けるusingは新規のリソース管理コードから: 既存のtry/finallyを一斉置換しない。新規箇所で慣れてから広げる- decorators は据え置き判断: フレームワーク依存が強いため、移行は単独 PR にしない
各ステップで tsc --noEmit と既存テストを必ず通してから次へ進んでください。
FAQ
Q. satisfies と型注釈 : T はどちらを使うべきですか?
A. 値の具体的な推論結果(リテラルのキーや値)を後段で使いたいなら satisfies、公開 API のシグネチャや関数の引数・戻り値の契約を明示したいなら : T です。設定オブジェクトやマッピングテーブルは satisfies、境界の契約は注釈、が実務の目安です。
Q. const type parameters はすべてのジェネリックに付けるべきですか? A. いいえ。渡したリテラルをキーや判別子として型レベルで後段利用する関数にだけ付けます。単に値を変換して返すだけの関数に付けると推論が無駄に narrow になり、かえって型エラーを誘発します。
Q. using 宣言は古い target でも使えますか?
A. Symbol.dispose / Symbol.asyncDispose の polyfill があれば ES2022 以下でも動作します。TypeScript 5.2 以降が必要です。解放はブロックスコープを抜けた瞬間に走る点に注意してください。
Q. 新 decorators と experimentalDecorators は併用できますか?
A. 同一プロジェクトでの混在は推奨しません。Angular / NestJS / TypeORM などレガシー decorators 前提のフレームワークを使う場合は experimentalDecorators: true を維持し、新規スタックでは標準 decorators を選んでください。
Q. 凝った条件型を書くべき場面はありますか? A. ライブラリの公開 API で、利用者の型安全性を担保する必要があるときは価値があります。一方、アプリ内部の補助関数では、テストで担保できる範囲を超えた型パズルは避けてください。判断軸は「型が間違っていたら実行時に壊れるか」です。
まとめ
- TypeScript 5.x の型システム進化は「文法追加」ではなく 「推論を殺さずに制約を足す手段の追加」。この視点で4機能を評価する
- satisfies は設定・マッピングで
: Tを置き換える最有力機能。型チェックはT、推論は右辺の値で - const type parameters はユーティリティ層に限定。「リテラルを後段で使うか」が基準
- using 宣言 はリソース解放を
try/finallyから解放。新規コードから段階導入する - 新 decorators はフレームワーク依存を確認してから。横断的関心事に限定する
- 最重要は 過剰な型付けを止めること。「型が壊れたら気づけるか」で型を厚くする場所を選ぶ
まず手元の設定オブジェクトを1つ satisfies に置き換えるところから始めてください。型設計の相談は みね(X) まで。
関連記事
- RSCとClient境界の引き方 ── 迷わない4軸判定 ── 型の境界と同じく、レンダリング境界も「どこで止めるか」の判断
- フロント性能改善の正しい順序: LCP→INP→CLS ── 型の最適化とビルド性能はトレードオフになる
