TypeScript を書いているのに、気づけば any と型アサーション(as)だらけ——それでは「型のあるJavaScript」でしかありません。本記事は、コピペしてすぐ試せる実践テクニックを10個だけ厳選しました。どれも現場で「バグを型で潰す」ために効くものばかりです。

結論から言うと、押さえるべき軸は3つ。

①推論を殺さないsatisfies / as const)、②状態を型で表す(判別可能ユニオン / Branded Types)、③新しい構文で書く量を減らすconst型パラメータ / NoInfer / using)。この順で見ていきます。

すべて TypeScript 5.x 系で動作します(各機能の導入バージョンは見出しに併記)。

独学に限界を感じたら

プログラミング教室がおすすめ。挫折せず最短で実力を伸ばせます。

PR

1. satisfies で「型チェック」と「推論」を両立する(4.9〜)

型注釈(: 型)を付けると型チェックは効きますが、各プロパティの型が広くなって推論を潰してしまいます。satisfies は「この型を満たすかチェックはするが、推論はそのまま残す」演算子です。

type RGB = readonly [number, number, number];
type Color = RGB | string;

// ✅ Record<string, Color> を満たすか検査しつつ、各値は最も狭い型のまま
const palette = {
  primary: "#5b8cff",
  danger: [255, 0, 0],
} satisfies Record<string, Color>;

palette.primary.toUpperCase(); // OK: primary は string と推論される
palette.danger[0].toFixed();   // OK: danger は number のタプルのまま

// ❌ 型注釈だと primary が Color に広がり .toUpperCase() が使えない
// const bad: Record<string, Color> = { ... };

「設定オブジェクトの形は縛りたいが、値の具体的な型は活かしたい」——そんな場面の定番です。まず satisfies を体に入れましょう。

2. as const で"魔法の文字列"をリテラル型に固める

as const を付けるとオブジェクトや配列が読み取り専用+リテラル型になります。定数から型を生成すれば、値と型の二重管理から解放されます。

const STATUS = {
  Todo: "todo",
  Doing: "doing",
  Done: "done",
} as const;

// 値から型を作る(enum 不要)
type Status = (typeof STATUS)[keyof typeof STATUS];
// => "todo" | "doing" | "done"

function move(next: Status) {}
move(STATUS.Done); // OK
move("gone");      // ❌ コンパイルエラー(タイポを型で検出)

enum の代わりに as const オブジェクトを使うのは、2026年の実務では定番の選択です。ランタイムのJSがそのままオブジェクトになり、tree-shaking もしやすくなります。

3. 判別可能ユニオン + never で「網羅漏れ」を型でブロック

共通のタグ(判別子)を持つユニオンにすると、switch で安全に分岐できます。さらに defaultnever に代入することで、ケースの追加忘れをコンパイルエラーにできます。

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; size: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.size ** 2;
    default: {
      // ここに来る値は never のはず。
      // Shape に "triangle" を足して case を書き忘れると↓でエラーになる
      const _exhaustive: never = shape;
      return _exhaustive;
    }
  }
}

この「exhaustiveness check(網羅性チェック)」は、状態が増えがちなReducerやAPIレスポンスの分岐で絶大な効果を発揮します。

4. Branded Types で「IDの取り違え」を防ぐ(公称型)

TypeScript は構造的型付けなので、stringUserIdPostId は区別されません。ブランド(幽霊プロパティ)を足すと、疑似的に公称型(nominal typing)を再現できます。

declare const brand: unique symbol;
type Brand<T, B> = T & { readonly [brand]: B };

type UserId = Brand<string, "UserId">;
type PostId = Brand<string, "PostId">;

const asUserId = (id: string) => id as UserId;

function fetchUser(id: UserId) {/* ... */}

fetchUser(asUserId("u_1")); // OK
fetchUser("u_1");          // ❌ ただの string は渡せない
const p: PostId = asUserId("u_1"); // ❌ UserId を PostId に代入できない

実行時のコストはゼロ(ただの string)なのに、引数の取り違えという頻出バグをコンパイル時に根絶できます。金額・ミリ秒・メールアドレスなど「形は同じでも意味が違う値」に効きます。

5. Record / Pick / Omit を使いこなす(ユーティリティ型)

型を毎回手書きするのは非効率です。組み込みのユーティリティ型で、既存の型から派生型を機械的に作りましょう

interface User {
  id: string;
  name: string;
  email: string;
  password: string;
}

// API で返す公開ユーザー(機密フィールドを除外)
type PublicUser = Omit<User, "password">;

// 入力フォームで使う一部だけ
type UserForm = Pick<User, "name" | "email">;

// id をキーにしたマップ
type UsersById = Record<string, User>;

// すべて任意 & 読み取り専用にした patch 用型
type UserPatch = Readonly<Partial<Pick<User, "name" | "email">>>;

Partial / Required / Readonly / Pick / Omit / Record / Exclude / Extract の8つは丸暗記して損なし。元の型を1つ直せば派生型が全部追従するのが最大の利点です。

6. テンプレートリテラル型で「文字列の形」を型にする

文字列そのもののフォーマットを型で表現できます。ルーティングやCSS変数名、イベント名などで威力を発揮します。

type Lang = "ja" | "en";
type Page = "home" | "about";

type Route = `/${Lang}/${Page}`;
// => "/ja/home" | "/ja/about" | "/en/home" | "/en/about"

const go = (path: Route) => {};
go("/ja/home"); // OK
go("/fr/home"); // ❌ 定義外の組み合わせはエラー

// イベント名を on〜 に変換する例
type Handler<E extends string> = `on${Capitalize<E>}`;
type ClickHandler = Handler<"click">; // "onClick"

7. const 型パラメータで引数を自動リテラル化(5.0〜)

ジェネリック関数に <const T> と書くと、呼び出し側が as const を付けなくてもリテラル型で推論してくれます。設定ビルダーやタプル操作の定番です。

// const なし
declare function make1<T>(config: T): T;
const a = make1({ mode: "dark" });
// a.mode は string に広がる

// const 型パラメータあり(TS 5.0〜)
declare function make2<const T>(config: T): T;
const b = make2({ mode: "dark" });
// b.mode は "dark" のまま! 呼び出し側の as const が不要

8. NoInfer<T> で推論の暴走を止める(5.4〜)

複数の引数から型パラメータを推論させたくない場面があります。NoInfer<T> を付けた引数は推論の材料から除外され、意図した候補だけで型が決まります。

// colors だけから C を推論し、defaultColor はそれに従わせたい
function createStreetLight<C extends string>(
  colors: C[],
  defaultColor: NoInfer<C>,
) {
  /* ... */
}

createStreetLight(["red", "yellow", "green"], "red");  // OK
createStreetLight(["red", "yellow", "green"], "blue"); // ❌ "blue" は候補外
// NoInfer が無いと C は "red"|"yellow"|"green"|"blue" に広がり、
// タイポの "blue" を検出できなかった

9. using で「後始末」を確実にする(5.2〜)

using 宣言を使うと、変数がスコープを抜けたときに自動で [Symbol.dispose]() を呼びます。try/finally の書き忘れによるリソースリークを防げます(トランスパイル対象と Symbol.dispose のポリフィルが必要)。

function openConnection() {
  console.log("open");
  return {
    query: (sql: string) => {/* ... */},
    [Symbol.dispose]() {
      console.log("close"); // スコープを抜けると自動で呼ばれる
    },
  };
}

function run() {
  using conn = openConnection();
  conn.query("SELECT 1");
} // ← ここで自動的に close される(例外が出ても確実に)

// 非同期の後始末には await using + [Symbol.asyncDispose]() を使う

10. 関数の型を再利用する(ReturnType / Awaited / Parameters)

既存の関数から戻り値・引数の型を抜き出して再利用できます。型を二重定義せず、実装が正となる状態を保てます。

async function fetchUser(id: string) {
  return { id, name: "Alice", age: 20 };
}

// Promise を剥がして戻り値の中身だけ取り出す
type User = Awaited<ReturnType<typeof fetchUser>>;
// => { id: string; name: string; age: number }

// 引数の型もそのまま使える
type FetchArgs = Parameters<typeof fetchUser>; // [id: string]

const cache = new Map<string, User>();

サーバのレスポンス型をフロントで再定義しがちですが、ReturnTypeAwaited を組み合わせれば「関数を1つ直せば型も追従」という理想状態に近づけます。

まとめ:型は"後付けの飾り"ではなく"設計そのもの"

今回の10個を一言でまとめると——推論を殺さず(1・2・7)、状態を型で表し(3・4・6)、派生と再利用で二重管理をなくし(5・10)、新構文で書く量を減らす(8・9)。

  • まず satisfies と as const で「推論を活かす」感覚を掴む
  • 判別可能ユニオン + never で状態の網羅漏れを潰す
  • Branded Types で意味の違う値の取り違えを防ぐ
  • ユーティリティ型と ReturnType/Awaited で型の二重管理をやめる

どれも anyas を減らし、「バグを実行前に型で捕まえる」ための道具です。まずは1つ、次のPRで satisfies から使ってみてください。リント環境を整える話は、Rust製オールインワンの Biome へ乗り換える記事も参考にどうぞ。

ESLint+Prettierを卒業|Biomeで爆速リント【2026】ESLint と Prettier、設定ファイルもプラグインも競合ももう限界——2026年のトレンドは Rust 製オールインワン「Biome」です。3コマンドで移行でき、フォーマットは Prettier の約35倍高速。コピペで使える biome.json と VS Code / CI 設定まで、10分で終わる乗り換え手順を丸ごと紹介します。frontendlab.magicgifted.com