TypeScriptで型注釈を付けたとたん、補完が利かなくなったり、具体的な値にアクセスできなくなった経験はありませんか?

typescript satisfiesって最近よく見るけど、結局何をしている演算子なの?
型注釈(: Type)を付けたらpalette.red.toUpperCase()が書けなくなって困った…
as const satisfiesってよく見るけど、なんで2つ並んでるの?それぞれ何の役割?

この記事では、TypeScript 4.9で導入されたsatisfies演算子について、基本構文から型注釈との違い、as const satisfiesの使い方、そしてアンチパターンまでをまとめて解説します。ざっくり言うと、satisfiesは「型に合っているかだけ検査して、推論結果はそのまま残す」演算子です。型注釈と型推論のいいとこ取りができる、実務でかなり使える機能です。

この記事は次のような方におすすめです。

この記事はこんな人におすすめ!
  • satisfiesを初めて使う、または何となく使っている中級者の方
  • 型注釈を付けると後段で具体プロパティにアクセスできず困った経験がある方
  • as const satisfiesの意味と使い所をきちんと理解したい方
  • 設定オブジェクトやマッピングオブジェクトを安全に書きたい方
  • satisfiesのアンチパターンを知って、誤用を避けたい方

この記事を読み終えるころには、satisfiesの存在意義、文法、頻出パターン、そして「使うべきでない場面」までを一気に整理できているはずです。設定オブジェクトを書くたびに迷っていた方は、書き方の引き出しが一気に増えるはずです。

それでは、順を追って詳しく見ていきましょう!

まずは動画解説を観る

Contents
  1. TypeScript satisfies演算子とは|型推論と型チェックを両立する仕組み
  2. 型注釈(: 型)との違い|satisfies が「型推論を残す」仕組み
  3. satisfies の具体的な使いどころ|実用ユースケース
  4. as const satisfies|widening を防ぎつつ型チェックする頻出パターン
  5. satisfies のアンチパターンと注意点|やってはいけない使い方
  6. satisfies と隣接構文の使い分け早見表
  7. よくある質問
  8. まとめ

TypeScript satisfies演算子とは|型推論と型チェックを両立する仕組み

まずは結論からです。satisfiesは「式が指定した型に合っているかをチェックしつつ、推論結果は変えずに残す」ためのTypeScript演算子です。TypeScript 4.9で導入されました。

これまでTypeScriptで型を扱うには、大きく2つの選択肢がありました。型注釈: Type)で値を型に合わせるか、型推論に任せて具体的な型を引き出すか。ただし、この2つは「安全性」と「具体性」のトレードオフでした。型注釈は型ミスを検知できるかわりにプロパティの具体型を「丸めて」しまうことがあり、型推論はリッチな具体型が得られるかわりに型ミスを検知できません。

satisfiesは、このトレードオフを解消する第3の選択肢です。雰囲気だけ先に掴むと、次のように書きます。

type Palette = Record<string, string | number[]>;

const palette = {
  red: '#ff0000',
  green: [0, 255, 0],
  blue: '#0000ff',
} satisfies Palette;

// OK:redはstringと推論されたまま
palette.red.toUpperCase();

// OK:greenはnumber[]と推論されたまま
palette.green.map((n) => n * 2);

このコードのポイントは次の通りです。

  • satisfies Paletteによって、オブジェクトがPalette型に合っているかチェックされます
  • それでいて、palette.redstringpalette.greennumber[]という具体型のまま扱えます
  • 型を満たしているかの「検査」と、推論結果の「保持」を同時に実現できます

この「型チェックは効くのに具体型も残る」のがどう嬉しいかは、このあと型注釈と並べて比較すると一気にはっきりします。

satisfies の基本構文(式 satisfies 型)

satisfiesは「式 satisfies 型」という形で書きます。左辺は値(式)、右辺は型です。

// 文法:値 satisfies 型
const value = { name: 'Taro', age: 30 } satisfies { name: string; age: number };

ここで重要なのは、satisfiesはコンパイル時にしか存在しないということです。TypeScriptが書いたコードをJavaScriptに変換するとき、satisfies以降は完全に削除されます。

// TypeScript
const config = {
  port: 3000,
  host: 'localhost',
} satisfies { port: number; host: string };

これを実際にコンパイルすると、JavaScriptは次のようになります。

// コンパイル後のJavaScript
const config = {
  port: 3000,
  host: 'localhost',
};

このコードのポイントは次の通りです。

  • satisfiesによる型チェックはコンパイル時のみ働きます
  • 実行時にはsatisfiesという演算子自体が存在しないので、ランタイムでの型検証はできません
  • 「型情報はコンパイル時に消える」というTypeScriptの基本原則は、satisfiesにも当てはまります

TypeScriptには「式 satisfies 型」のように、コンパイル時にだけ意味を持つ構文がいくつもあります。型注釈(:)、型アサーション(as)、satisfies、as constなどが代表例です。これらはすべてJavaScriptへの変換時に取り除かれるため、実行時の挙動には一切影響しません。

なぜ TypeScript 4.9 で導入されたのか

satisfiesが生まれた背景は、ひと言で言うと「型注釈と型推論のトレードオフを解消したかった」からです。

たとえば、複数の色を扱う設定オブジェクトを考えます。値は文字列のときもあれば、RGB配列のときもあります。

const palette = {
  red: '#ff0000',
  green: [0, 255, 0],
  blue: '#0000ff',
};

palette.red.toUpperCase(); // OK:redはstringと推論されている
palette.green.map((n) => n * 2); // OK:greenはnumber[]と推論されている

型推論に任せれば、palette.redstringpalette.greennumber[]として扱えて快適です。ただし、これだと書いたオブジェクトが「期待する形」に合っているかは一切チェックされません。たとえばredにうっかり数値を入れても、greenを書き忘れても、TypeScriptは何も言ってくれません。

そこで型注釈を付けたくなりますが、後ほど見るように型注釈には別の問題があります。この問題を解決するために導入されたのがsatisfiesです。

なかむぅ
なかむぅ
そもそも「型推論」がどう動いているのか曖昧な方は、こちらの記事で復習しておくとsatisfiesの理解が一段深まりますよ。
【TypeScript】型推論ってなに?型を書かなくても型が決まる仕組みを解説
【TypeScript】型推論ってなに?型を書かなくても型が決まる仕組みを解説TypeScriptの型推論を初心者向けにやさしく解説。型を書かなくても型が決まる仕組みや、省略できる場面・書くべき場面がすっきりわかります。 ...

型注釈(: 型)との違い|satisfies が「型推論を残す」仕組み

ここからが本記事の中核です。型注釈とsatisfiesは「型に合っているかをチェックする」という点では同じ働きをします。違いはチェックした後にあります。型注釈は値を型に「丸めて」しまうのに対し、satisfiesは推論結果をそのまま残す。この違いを、同じオブジェクトで比較しながら見ていきます。

型注釈は値を型に「丸める」

先ほどのpaletteに、型注釈を付けてみます。値は文字列または数値配列なので、型はRecordが妥当に見えます。

type Palette = Record<string, string | number[]>;

const palette: Palette = {
  red: '#ff0000',
  green: [0, 255, 0],
  blue: '#0000ff',
};

// エラー: Property 'toUpperCase' does not exist on type 'string | number[]'.
palette.red.toUpperCase();

// エラー: Property 'map' does not exist on type 'string | number[]'.
palette.green.map((n) => n * 2);

このコードのポイントは次の通りです。

  • 型注釈を付けると、palette全体がPalette型として扱われます
  • 個々のプロパティの具体的な情報が失われpalette.redpalette.greenstring | number[]に丸められます
  • TypeScriptはredstringgreennumber[]だったことを「忘れて」しまうので、toUpperCase()map()が呼べません

これが、型注釈の「丸める」挙動です。型チェックという安全性は得られますが、引き換えに具体的な値の情報を失います。書き手が「redは文字列だ」と知っていても、型注釈を付けた瞬間にコードを書くたびに型ガードや絞り込みが必要になり、かなり煩雑になります。

satisfies は「型に合うか」だけを検査する

同じオブジェクトをsatisfiesで書くと、挙動が大きく変わります。

type Palette = Record<string, string | number[]>;

const palette = {
  red: '#ff0000',
  green: [0, 255, 0],
  blue: '#0000ff',
} satisfies Palette;

// OK:redはstringと推論されたまま、文字列メソッドが呼べる
palette.red.toUpperCase();

// OK:greenはnumber[]と推論されたまま、配列メソッドが呼べる
palette.green.map((n) => n * 2);

// エラー:型に合わない値はちゃんと検知される
const wrong = {
  red: 123, // Type 'number' is not assignable to type 'string | number[]'.
} satisfies Palette;

このコードのポイントは次の通りです。

  • satisfies Paletteでも、型注釈と同じく型に合わない値(red: 123など)はちゃんとエラーになります
  • 違いはここからで、palette.redstringpalette.greennumber[]という具体型のまま残ります
  • そのため、型注釈では呼べなかったtoUpperCase()map()が、絞り込みなしでそのまま呼べます

つまり、型のチェックという安全性は型注釈と同じだけ確保しつつ、具体型という利便性も失わない。これがsatisfiesの最大の価値です。型注釈で消えていた情報がそのまま使えるので、後段のコードを書くときに型ガードやasでの絞り込みをする必要がなくなります。

比較表で整理

ここまでの違いを表に整理しておきます。

観点 型注釈(: 型 satisfies
型エラーの検知 する する
推論結果の保持 しない(型に丸める) する
後段でのプロパティアクセス 注釈した型の範囲に限定される 具体的な値の型で扱える
適した場面 関数引数、API境界、変数の型を固定したいとき ローカルなオブジェクトリテラル、設定オブジェクト

まずは「型注釈も satisfies も型チェックはする。違いは推論を残すかどうか」と覚えればOKです。

なお、型エイリアス(type)の基本的な書き方が曖昧な方は、先に押さえておくとsatisfiesの説明がスッと入ってきます。

なかむぅ
なかむぅ
型エイリアス(type)の使い方を整理したい方は、こちらの記事から確認してみてください。
TypeScriptのtypeエイリアスとは?基本の書き方と使いどころを整理するTypeScriptのtype(型エイリアス)の基本を解説。オブジェクトやユニオン型の書き方、interfaceとの違いを整理しました。型の再利用でコードの可読性を高めるメリットと使いどころがわかります。...

satisfies の具体的な使いどころ|実用ユースケース

satisfiesは抽象的な概念だけだとピンと来づらいので、ここからは実務でよく出会う3つのパターンをコードで見ていきます。いずれも「型注釈だと推論結果が失われて不便」という共通の課題を、satisfiesが解決する場面です。

設定オブジェクトを安全に書く

ルーティング設定やテーマ設定など、「形は決まっているが、具体的な値も使いたい」場面でsatisfiesは威力を発揮します。

type RouteConfig = Record<string, {
  method: 'GET' | 'POST';
  auth: boolean;
}>;

const routes = {
  '/users': { method: 'GET', auth: false },
  '/users/me': { method: 'GET', auth: true },
  '/posts': { method: 'POST', auth: true },
} satisfies RouteConfig;

// 実際に書いたキーが型として残るので、補完も効くし存在チェックも効く
const userRoute = routes['/users']; // OK
const meRoute = routes['/users/me']; // OK

// エラー:存在しないパスはちゃんと検知される
routes['/unknown']; // Property '/unknown' does not exist on type ...

このコードのポイントは次の通りです。

  • method'PUT'を書けばエラーになる、という型チェックは型注釈でも同じく効きます
  • satisfiesならではの利点は、routesのキー(/users/users/meなど)が具体的なキー名として残ることです
  • 型注釈で: RouteConfigを直接付けるとキーはstringに丸められ、存在しないパスにアクセスしてもエラーになりません。satisfiesなら実際に書いたパスだけが許可され、補完にも実在するパスだけが出てきます

マッピングオブジェクトでキーの網羅性をチェックする

ユニオン型をキーにしたRecordは、satisfiesとの相性が抜群です。「定義済みの色すべてに対して値を用意する」ような場面で使えます。

type Color = 'red' | 'green' | 'blue';

const colorHex = {
  red: '#ff0000',
  green: '#00ff00',
  blue: '#0000ff',
} satisfies Record<Color, string>;

// エラー:'blue' プロパティが欠けている
const incomplete = {
  red: '#ff0000',
  green: '#00ff00',
} satisfies Record<Color, string>;

// colorHex.red は 'red'(リテラル型)として扱えるので、より厳密に使える
const red: '#ff0000' = colorHex.red;

このコードのポイントは次の通りです。

  • Recordでキー網羅をチェックできる点(キーが足りないとエラー)は、型注釈でも同じです
  • satisfiesならではの利点は、網羅チェックを効かせつつcolorHex.redstringに丸められず具体的な値の型として残ることです
  • 型注釈: RecordだとcolorHex.redの型はただのstringになりますが、satisfiesなら推論結果が保たれるため、後段でより厳密に扱えます

ユニオン型のキー網羅は、タグ付きユニオンと組み合わせるとさらに強力です。

なかむぅ
なかむぅ
ユニオン型・タグ付きユニオンの全体像を押さえると、satisfiesの網羅性チェックの威力がより理解しやすくなります。
TypeScriptのユニオン(Union)型とは?型ガードで「どの型か」を安全に絞り込む方法
TypeScriptのユニオン(Union)型とは?型ガードで「どの型か」を安全に絞り込む方法TypeScriptのユニオン型とは何かを基礎から解説。typeof・in・instanceofを使った型ガードの違いと使い分けも初心者向けにわかりやすく整理します。...
【TypeScript】Discriminated Unionとは?タグで型を絞り込む仕組みを解説TypeScriptのDiscriminated Unionを初心者向けに解説。タグによる型の絞り込み、switch文、never型の使い方までやさしく理解できます。...

関数の戻り値で型を絞り込む

関数の戻り値型を「広めに宣言」しつつ、内部実装では「具体型として扱いたい」場面でも使えます。

type ApiResult = {
  status: number;
  body: unknown;
};

function buildResult() {
  const result = {
    status: 200,
    body: { message: 'OK', items: [1, 2, 3] },
  } satisfies ApiResult;

  // 内部実装ではbodyの具体型を使える
  console.log(result.body.message.toUpperCase());
  console.log(result.body.items.length);

  return result;
}

このコードのポイントは次の通りです。

  • ApiResult型に合っているかチェックされるため、statusの型ミスなどを検知できます
  • bodyを型注釈でunknownにしてしまうと、result.body.messageへのアクセスに毎回型ガードが必要になります
  • satisfiesならresult.bodyが具体的な型({ message: string; items: number[] })として残るため、関数内のコードがすっきりします

as const satisfies|widening を防ぎつつ型チェックする頻出パターン

ここまで見てきたsatisfiesですが、実は単体だと困る場面があります。それは、リテラル値が一般的な型に「広がって」しまうケースです。これを解決するのがas const satisfiesという頻出パターンです。

widening とは何か(簡単な復習)

widening(ワイドニング)とは、リテラル値の型がstringnumberなどの広い型に広がる現象のことです。

// constで宣言したプリミティブは、リテラル型のまま
const a = 'red'; // 型は 'red'

// letで宣言すると、stringに広がる(widening)
let b = 'red'; // 型は string

// オブジェクトのプロパティも同じく、デフォルトでは広がる
const obj = { name: 'red' }; // name の型は string('red' ではない)

このコードのポイントは次の通りです。

  • constで宣言したプリミティブは、リテラル型として狭いまま保たれます
  • letやオブジェクトのプロパティでは、デフォルトでより広い型(stringnumberなど)に推論されます
  • リテラル型を保ちたい場面では、as constで「広がり」を止めます

ここで押さえておきたいのは「オブジェクトのプロパティは、デフォルトでは値が広い型に丸められる」という点だけです。wideningとas constそのものの仕組みは、リテラル型を扱う記事で詳しく解説しています。

なかむぅ
なかむぅ
wideningとas constの挙動をもっと深く理解したい方は、こちらの記事を先に読むとスッキリしますよ。
TypeScriptのas constとは?具体的な使い方と2つの注意点TypeScriptのコードを読んでいると、as const という見慣れない記述に出くわすことがあります。 この...

as const satisfies の挙動を4パターンで比較

同じオブジェクトに対して、「as constあり/なし × satisfiesあり/なし」の4パターンで挙動がどう変わるかを見てみましょう。

type Theme = {
  primary: string;
  secondary: string;
};

// パターン1:as const なし、satisfies なし
const theme1 = {
  primary: '#ff0000',
  secondary: '#00ff00',
};
// 型: { primary: string; secondary: string }
// → 推論結果は得られるが、型チェックは効かない(Themeに合っているかは検査されない)

// パターン2:as const あり、satisfies なし
const theme2 = {
  primary: '#ff0000',
  secondary: '#00ff00',
} as const;
// 型: { readonly primary: '#ff0000'; readonly secondary: '#00ff00' }
// → リテラル型で狭く保たれるが、Theme型に合っているかの検査はない

// パターン3:as const なし、satisfies あり
const theme3 = {
  primary: '#ff0000',
  secondary: '#00ff00',
} satisfies Theme;
// 型: { primary: string; secondary: string }
// → Theme型に合っているか検査されるが、値はstringに広がる

// パターン4:as const あり、satisfies あり ★定数オブジェクト向けの頻出パターン
const theme4 = {
  primary: '#ff0000',
  secondary: '#00ff00',
} as const satisfies Theme;
// 型: { readonly primary: '#ff0000'; readonly secondary: '#00ff00' }
// → リテラル型で狭く保ちつつ、Theme型に合っているかも検査される

4パターンの違いを表にすると次の通りです。

パターン as const satisfies 型エラー検知 リテラル型の保持
1 なし なし しない しない
2 あり なし しない する
3 なし あり する しない
4 あり あり する する

このコードのポイントは次の通りです。

  • パターン4(as const satisfiesは、型チェックとリテラル型保持を両立できる定数オブジェクト向けの定番パターンです
  • as constが値を狭めて、satisfiesが型に合っているかを検査する、という役割分担ができています
  • 「順序」も重要で、必ずas const satisfies Typeの順に書きます(逆順だと意図通りに動きません)

ここで注意したいのは、as constを付けるとプロパティがreadonlyになるため、後から値を書き換えたい設定オブジェクトには向かないという点です。あくまで定数として使うオブジェクトに適したパターンと押さえておきましょう。

enum の代替としての as const satisfies

TypeScriptのenumは便利な反面、独自の挙動や非推奨論争が続いています。その代替として、as const satisfiesを使ったオブジェクトリテラルがよく推奨されます。

type StatusCode = Record<string, number>;

// enumの代わりにオブジェクト + as const satisfies
const HttpStatus = {
  OK: 200,
  NotFound: 404,
  InternalServerError: 500,
} as const satisfies StatusCode;

// 値はリテラル型として狭く保たれる
const status = HttpStatus.OK; // 型は 200(number ではない)

// 値の集合をユニオン型として取り出せる
type HttpStatusValue = typeof HttpStatus[keyof typeof HttpStatus];
// 型は 200 | 404 | 500

このコードのポイントは次の通りです。

  • as constオブジェクトはJavaScriptの普通のオブジェクトとして書けるため、コンパイル結果がそのまま読みやすい形になります
  • enumも実行時にはオブジェクトを生成しますが、数値enumの「逆マッピング」など独自のコンパイル結果を持つため、シンプルな定数マップで十分な場面ではas constの方が挙動を追いやすい場合があります
  • as constにより値はリテラル型で保持され、satisfiesによりキーや値の型が正しいか検査されます
  • typeof HttpStatus[keyof typeof HttpStatus]でユニオン型を取り出せるため、enumの代用としても十分機能します

satisfies のアンチパターンと注意点|やってはいけない使い方

便利な機能ほど乱用されがちです。satisfiesも例外ではなく、「とりあえずsatisfies」が逆効果になる場面もあります。ここでは、特に注意したい2つのアンチパターンを整理します。

型注釈をすべて satisfies に置き換えるのは過剰

satisfiesを知った直後にやりがちなのが、「型注釈を全部satisfiesにすればいい」という発想です。ただ、関数引数やAPI境界(外部に公開する関数の引数・戻り値)では型注釈の方が適切な場面が多いです。

// アンチパターン:関数引数にsatisfiesを使おうとする(そもそも使えない)
// function greet(name satisfies string) { ... } // 構文エラー

// 関数引数は型注釈が自然
function greet(name: string) {
  return `Hello, ${name}`;
}

// 関数の戻り値の型も、明示したい場合は型注釈が分かりやすい
function getUser(): { id: number; name: string } {
  return { id: 1, name: 'Taro' };
}

このコードのポイントは次の通りです。

  • satisfiesは式に対して使う演算子で、関数引数のような宣言位置には書けません
  • 関数引数は「呼び出し側に対して制約をかける」役割なので、型注釈の方が意図が明確に伝わります
  • API境界も、型注釈で型を「契約として固定する」運用が向いています

ここで注意したいのは、satisfiesと型注釈は競合する機能ではなく、適材適所で使い分けるものだという点です。設定オブジェクトや一時的なリテラルではsatisfies、関数の入り口や型を契約として固定したい場面では型注釈、という棲み分けが実務では自然です。

satisfies は実行時の型チェックではない

これも非常に多い誤解です。satisfiesはコンパイル時のチェックでしかなく、実行時の安全性は何も保証しません

type User = {
  id: number;
  name: string;
};

// API レスポンスを satisfies で「安全に」しているつもりのコード
async function fetchUser() {
  const response = await fetch('/api/user');
  const data = await response.json(); // 戻り値の型は any

  // data は any なので、satisfies User は型チェックを素通りしてしまう
  const user = data satisfies User; // 実行時には何のチェックもされない

  return user;
}

このコードのポイントは次の通りです。

  • response.json()の戻り値は通常anyとして扱われるため、data satisfies Userと書いても実質的な型チェックになりません
  • 実行時には、APIが{ id: 'abc', name: null }のような壊れた値を返してもsatisfiesは検知できません
  • 外部入力(API、フォーム、ファイルなど)の安全性が必要な場面では、別途、実行時バリデーションが必要です

ここで注意したいのは、satisfiesの「型に合うか確認」という説明だけ聞くと、つい実行時にもチェックしてくれそうな印象を持ってしまうことです。あくまでコンパイル時に消える型情報の話だと押さえておきましょう。実行時に安全性を担保したい場合は、自前の型ガード関数やスキーマバリデーションを使うことになります。

satisfies と隣接構文の使い分け早見表

ここまで型注釈・satisfies・as constを見てきましたが、もう1つ、似た位置に書く構文としてas(型アサーション)があります。最後に、これら4つの構文の位置付けを早見表で整理しておきます。

構文 役割 推論結果 主な使い所
型注釈(: 型 値を型に合わせる 注釈した型に固定される 関数引数、API境界、変数の型を固定したいとき
型アサーション(as 型 型を「ねじ伏せて」上書きする 指定した型に強制される 型推論より自分の方が型を知っている場面(要注意)
as const 値をリテラル型として固定する 狭いリテラル型として保持される 定数オブジェクトや配列のリテラル化
satisfies 型 値が型に合うかを検査する 推論結果は残る 設定オブジェクト、マッピングオブジェクト

各構文を1行サンプルで並べると、違いがより鮮明になります。

const a: { name: string } = { name: 'Taro' };           // 型注釈
const b = { name: 'Taro' } as { name: string };          // 型アサーション
const c = { name: 'Taro' } as const;                     // as const
const d = { name: 'Taro' } satisfies { name: string };   // satisfies

この4つはそれぞれ役割が違います。とくにasはsatisfiesと混同されやすいですが、asは型を強制的に上書きするのに対し、satisfiesは型に合わなければエラーになるという根本的な違いがあります。

なかむぅ
なかむぅ
asがどんな構文なのか、satisfiesとどう使い分けるのか、asを安全に使う代替手段については、こちらの記事でまとめて解説しています。
TypeScriptのas完全ガイド|型アサーションとキャストの違いと安全な使い方TypeScriptの `as`(型アサーション)の書き方、キャストとの違い、危険性と安全な使い方を初心者向けに解説。`<型>` 構文、ダブルアサーション、`as const`・`satisfies`との使い分けまで網羅。...
なかむぅ
なかむぅ
as const単体の詳しい使い方は、こちらの記事をどうぞ。リテラル型の固定の仕組みが分かります。
TypeScriptのas constとは?具体的な使い方と2つの注意点TypeScriptのコードを読んでいると、as const という見慣れない記述に出くわすことがあります。 この...

よくある質問

satisfies は TypeScript のどのバージョンから使えますか?

satisfiesはTypeScript 4.9で導入されました。それ以前のバージョン(4.8以下)では構文エラーになります。プロジェクトのpackage.jsonでTypeScriptのバージョンを確認し、4.9以上であれば問題なく使えます。古いバージョンを使い続ける必要がある場合は、ジェネリクスを使ったヘルパー関数(function asConfig(value: T): Tのような形)で似た挙動を再現する手もありますが、4.9以上にアップグレードしてsatisfiesを使う方がシンプルです。

satisfies は型注釈(: 型)の代わりになりますか?

場面によります。ローカルなオブジェクトリテラル設定オブジェクトではsatisfiesが向いていることが多いです。型に合っているかを検査しつつ、推論結果を残せるからです。一方で、関数引数API境界(外部に公開する関数の戻り値型など)では、型注釈の方が適切です。「呼び出し側に対する契約」を明示する意図が強い場面では、型注釈で型を固定する運用が自然です。

as const satisfies を使うべき場面はどんなときですか?

リテラル値の集合を定数化したいが、型エラーで早期検知もしたい」場面が代表例です。HTTPステータスコードやテーマ定義のように、決まった値を持つ定数オブジェクトでよく使われます。enumの代替としても有効で、as constオブジェクトはJavaScriptの普通のオブジェクトとして書けるため、コンパイル結果が読みやすいというメリットがあります。

satisfies で実行時の型チェックはできますか?

できません。satisfiesはコンパイル時のみ動作する型機能で、TypeScriptがJavaScriptに変換されるときには完全に削除されます。実行時にAPIレスポンスやユーザー入力の型を検証したい場合は、自前の型ガード関数やスキーマバリデーションなど、別の実行時検証の仕組みを使う必要があります。

satisfies と as はどう違いますか?

ひと言で言うと、asは型を「ねじ伏せる」、satisfiesは型に「合うかを確認する」という違いです。asはコンパイラの推論結果を無視して指定した型に強制するため、間違えると実行時エラーの原因になります。一方、satisfiesは型に合わなければエラーになるので、型システムを欺くことはありません。安全性の面では、satisfiesの方が基本的に望ましい選択肢です。

satisfies を関数の引数に使えますか?

通常は使いません。satisfiesは式に対して使う演算子で、関数引数のような宣言位置には書けません。関数引数の型を指定したい場合は、型注釈((name: string) => ...)を使うのが自然です。satisfiesが活躍するのは、値(オブジェクトリテラルなど)に対して型チェックをかけたい場面です。

satisfies を使うと型推論が壊れることはありますか?

通常は壊れません。satisfiesは推論結果を保持する仕組みです。ただし、右辺の型を広く書きすぎると、意図しない推論結果になることがあります。たとえばsatisfies Recordのように緩い型を指定すると、せっかくの型チェックが効かず、ほぼ意味がなくなります。型は具体的に書くほど効果を発揮するので、可能な範囲で狭いユニオン型やリテラル型を組み合わせるのがコツです。

【付録】さらに学びを深めるためのリソース


さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。

おすすめの書籍

ゼロからわかる TypeScript入門


技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。

変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。

プロを目指す人のためのTypeScript入門


技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。

オンラインで参照できる公式ドキュメント

TypeScript公式ハンドブック


https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。

TypeScript Deep Dive


https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。

TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。


まとめ

TypeScriptのsatisfies演算子について、基本構文から実用パターン、アンチパターンまで一通り見てきました。要点を振り返ります。

  • satisfiesは「式が型に合っているかを検査しつつ、推論結果はそのまま残す」TypeScript 4.9以降の演算子です
  • 型注釈(: 型)も型チェックはしますが、値を型に丸めてしまいます。satisfiesは型チェックを効かせたまま具体型を残せる点が最大の違いです
  • 設定オブジェクト、マッピングオブジェクト、関数内の生成オブジェクトなど、ローカルなオブジェクトリテラルとの相性が抜群です
  • as const satisfiesは、リテラル型として狭く保ちつつ型チェックも効かせる頻出パターンで、enumの代替としても使えます
  • satisfiesはコンパイル時のチェックでしかなく、実行時の安全性は保証しません。外部入力の検証には別途バリデーションが必要です

satisfiesを使いこなせると、「型注釈で具体型が消えて困る」というモヤモヤから解放されます。まずは設定オブジェクトを1つ書き換えてみるところから始めてみてください。書き心地の違いが、すぐに体感できるはずです。

※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。