TypeScript 5.x最新機能まとめ2026|Decorators・satisfies・const型パラメータ
TypeScript 5.xの進化
TypeScript 5系のリリースノートを毎回読んでいますが、satisfiesを初めて知ったときの衝撃は忘れられません。「型アノテーションをつけると推論が死ぬ」問題に何年も苦しめられてきたので、解決策が1キーワードで済むことに驚きました。
この記事では5.0から最新の5.7まで、実務で本当に使う機能だけを厳選して解説します。リリースノートの全訳ではなく「これを知っていれば日常のコーディングが変わる」ものに絞りました。
satisfies演算子(TS 5.0)
satisfiesは、型チェックを行いつつ、推論された型を保持する演算子です。
問題:型アノテーションでは推論が失われる
type Colors = Record<string, string | string[]>;
// 型アノテーション: stringかstring[]かわからなくなる
const palette: Colors = {
red: '#ff0000',
green: '#00ff00',
blue: ['#0000ff', '#0000cc'],
};
// ❌ string | string[] なので toUpperCase が使えない
palette.red.toUpperCase();
解決:satisfiesで推論を保持
const palette = {
red: '#ff0000',
green: '#00ff00',
blue: ['#0000ff', '#0000cc'],
} satisfies Colors;
// ✅ redはstringと推論される
palette.red.toUpperCase(); // OK
// ✅ blueはstring[]と推論される
palette.blue.map(c => c.toUpperCase()); // OK
// ✅ 存在しないキーはエラー
// palette.yellow; // Error
実践的な使用例
// API レスポンスの型定義
type ApiConfig = Record<string, {
url: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
auth?: boolean;
}>;
const apiEndpoints = {
getUsers: {
url: '/api/users',
method: 'GET',
},
createUser: {
url: '/api/users',
method: 'POST',
auth: true,
},
updateUser: {
url: '/api/users/:id',
method: 'PUT',
auth: true,
},
} satisfies ApiConfig;
// 各エンドポイントの型が正確に推論される
apiEndpoints.getUsers.method; // 'GET'('GET' | 'POST' | ... ではない)
TC39 Decorators(TS 5.0)
TypeScript 5.0でStage 3のDecoratorが正式サポートされました。
// クラスメソッドのログデコレータ
function log<This, Args extends unknown[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
const methodName = String(context.name);
return function (this: This, ...args: Args): Return {
console.log(`${methodName} called with:`, args);
const result = target.call(this, ...args);
console.log(`${methodName} returned:`, result);
return result;
};
}
// バリデーションデコレータ
function validate<This, Args extends unknown[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext
) {
return function (this: This, ...args: Args): Return {
for (const arg of args) {
if (arg === null || arg === undefined) {
throw new Error(`${String(context.name)}: 引数がnull/undefinedです`);
}
}
return target.call(this, ...args);
};
}
class UserService {
@log
@validate
createUser(name: string, email: string) {
return { id: crypto.randomUUID(), name, email };
}
}
Auto-Accessor(自動アクセサ)
class User {
accessor name: string; // getter/setterが自動生成
constructor(name: string) {
this.name = name;
}
}
// デコレータと組み合わせ
function observable<This, Value>(
target: ClassAccessorDecoratorTarget<This, Value>,
context: ClassAccessorDecoratorContext<This, Value>
) {
return {
get(this: This): Value {
console.log(`Reading ${String(context.name)}`);
return target.get.call(this);
},
set(this: This, value: Value) {
console.log(`Setting ${String(context.name)} to`, value);
target.set.call(this, value);
},
};
}
class Settings {
@observable
accessor theme: 'light' | 'dark' = 'light';
}
const型パラメータ(TS 5.0)
ジェネリック関数で、引数をリテラル型として推論させる修飾子です。
// constなしの場合
function routes<T extends Record<string, string>>(config: T): T {
return config;
}
const r1 = routes({
home: '/',
about: '/about',
});
// r1の型: { home: string; about: string }
// → 値がstring型に拡大されてしまう
// constありの場合
function routesConst<const T extends Record<string, string>>(config: T): T {
return config;
}
const r2 = routesConst({
home: '/',
about: '/about',
});
// r2の型: { readonly home: "/"; readonly about: "/about" }
// → リテラル型が保持される!
実践例:型安全なイベントシステム
function defineEvents<const T extends Record<string, (...args: any[]) => void>>(
events: T
): T {
return events;
}
const events = defineEvents({
click: (x: number, y: number) => {},
keydown: (key: string) => {},
resize: (width: number, height: number) => {},
});
// events.click の引数型が正確に推論される
type ClickHandler = typeof events.click;
// (x: number, y: number) => void
using宣言(TS 5.2)
usingは、スコープを抜けたときに自動的にリソースを解放する宣言です。
// Symbol.disposeを実装
class DatabaseConnection implements Disposable {
constructor(private url: string) {
console.log(`Connected to ${url}`);
}
query(sql: string) {
return [{ id: 1, name: 'test' }];
}
[Symbol.dispose]() {
console.log(`Disconnected from ${this.url}`);
}
}
function getUsers() {
using db = new DatabaseConnection('postgres://localhost/mydb');
const users = db.query('SELECT * FROM users');
return users;
// ← ここで自動的に db[Symbol.dispose]() が呼ばれる
}
非同期版:await using
class FileHandle implements AsyncDisposable {
static async open(path: string) {
const handle = new FileHandle(path);
await handle.init();
return handle;
}
private constructor(private path: string) {}
private async init() {
// ファイルを開く処理
}
async read(): Promise<string> {
return 'file contents';
}
async [Symbol.asyncDispose]() {
// ファイルを閉じる処理
console.log(`Closed ${this.path}`);
}
}
async function processFile() {
await using file = await FileHandle.open('/tmp/data.txt');
const content = await file.read();
return content;
// ← 自動的に file[Symbol.asyncDispose]() が呼ばれる
}
Isolated Declarations(TS 5.5)
--isolatedDeclarationsフラグを有効にすると、型推論に頼らず明示的な型注釈が必要になります。
// ❌ isolatedDeclarations: true ではエラー
export function add(a: number, b: number) {
return a + b; // 戻り値型が推論に依存
}
// ✅ 戻り値型を明示
export function add(a: number, b: number): number {
return a + b;
}
なぜ重要か
- ビルド高速化: 各ファイルを独立して.d.tsに変換可能
- 並列ビルド: ファイル間の依存なく宣言ファイル生成
- monorepo: パッケージ間の型生成が高速化
正規表現の型チェック(TS 5.5)
// TS 5.5以降: 正規表現リテラルの構文チェック
const re1 = /hello/; // OK
const re2 = /hello/g; // OK
// ❌ 無効なフラグ
// const re3 = /hello/z; // Error: Unknown regular expression flag
// ❌ 無効なエスケープ
// const re4 = /\p/; // Error: Invalid escape
// ✅ Unicode property escapeも検証
const re5 = /\p{Script=Hiragana}/u; // OK(ひらがなにマッチ)
Import Attributes(TS 5.3)
// JSONインポートに型情報を付与
import config from './config.json' with { type: 'json' };
// CSSモジュール
import styles from './styles.css' with { type: 'css' };
// 型定義での使用
declare module '*.json' {
const value: Record<string, unknown>;
export default value;
}
NoInferユーティリティ型(TS 5.4)
特定の位置での型推論を意図的に無効化する型です。
// NoInferなし: secondがfirstの型推論に影響する
function createFSM<S extends string>(config: {
initial: S;
states: Record<S, { on: Record<string, S> }>;
}) {}
// NoInferあり: initialの値はstatesのキーから推論
function createFSM<S extends string>(config: {
initial: NoInfer<S>; // ここでは推論しない
states: Record<S, { on: Record<string, NoInfer<S>> }>;
}) {}
createFSM({
initial: 'idle', // statesのキーからのみ推論
states: {
idle: { on: { start: 'running' } },
running: { on: { stop: 'idle' } },
},
});
tsconfig.jsonの新オプション
TS 5.0〜5.7で追加された重要オプション
{
"compilerOptions": {
// TS 5.0: バンドラー向けモジュール解決
"moduleResolution": "bundler",
// TS 5.0: カスタム条件
"customConditions": ["development"],
// TS 5.2: ESMデフォルト
"module": "nodenext",
// TS 5.4: ルートディレクトリの推論改善
"rootDir": "./src",
// TS 5.5: 独立宣言ファイル
"isolatedDeclarations": true,
// TS 5.6: null/undefinedの厳密チェック強化
"strictBuiltinIteratorReturn": true,
// 常に有効にすべきオプション
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
まとめ
TypeScript 5.x系の注目機能:
| 機能 | バージョン | インパクト |
|---|---|---|
| satisfies | 5.0 | ★★★★★ |
| Decorators | 5.0 | ★★★★☆ |
| const型パラメータ | 5.0 | ★★★★☆ |
| using宣言 | 5.2 | ★★★★☆ |
| Import Attributes | 5.3 | ★★★☆☆ |
| NoInfer | 5.4 | ★★★☆☆ |
| Isolated Declarations | 5.5 | ★★★★☆ |
| 正規表現型チェック | 5.5 | ★★★☆☆ |
個人的な優先度
すぐにプロジェクトに導入すべきもの:
- satisfies — 今日から使える。型アノテーション vs 型推論の永遠の悩みが消える
- const型パラメータ —
as const地獄からの解放 - using宣言 — DB接続やファイルハンドルのクリーンアップが確実になる
「知っておくといい」もの:
- Decorators — ライブラリ作者やNestJS使いには必須、普通のアプリ開発では出番が少ない
- NoInfer
— ライブラリの型定義で威力を発揮する上級者向け機能
「まだ様子見」:
- Isolated Declarations — モノレポのビルド高速化に効くが、既存プロジェクトへの導入コストが高い