TypeScript satisfiesを完全理解|asと型注釈の使い分け
as で型エラーを握りつぶしたら、まったく別の場所でコードが壊れた。TypeScript の satisfies 演算子を覚えると、こうした事故をその場で防げるようになります。
satisfies って結局何をする演算子なの?as や型注釈(:)で書くのと何が違うのかわからない。
: を付けると補完が効かなくなり、as を使うと間違った値も通ってしまう。安全に値を型で縛る書き方が知りたい。
as const satisfies という書き方をよく見かけるけれど、なぜこの順番なのか、何が嬉しいのかが腹落ちしていない。
satisfies は、値がある型に当てはまるかを検査しつつ、変数の型は推論された具体的なものを残してくれる演算子です。: は具体型を奪い、as は検査を奪う——この違いさえつかめば、三者の使い分けに迷わなくなります。実コードと推論結果の対比を通して、その判断基準を最後まで整理していきます。
この記事は次のような方におすすめです。
satisfiesが何をする演算子で、何が嬉しいのかを知りたい方- 型注釈(
:)やasとの違い・使い分けではっきりした基準が欲しい方 as const satisfiesの定番パターンを仕組みから理解したい方asで握りつぶしていたコードを、より安全な書き方に直したい方
読み終えるころには、設定オブジェクトや定数を「型で縛りつつ補完も推論も残す」書き方が自分で選べるようになり、as 由来の見えないバグを未然に減らせます。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
4年間ずっと年収260万だったエンジニアが、転職で510万になるまでの全記録
satisfies演算子とは|「検査はするが型は奪わない」
satisfies は、値がある型に代入可能かを検査するが、変数の型はその型に固定せず、推論された具体型のまま残す演算子です。型チェックの恩恵を受けながら、リテラル型やキーごとの具体的な情報を失わずに済むのが最大のうまみです。
最小の構文は 値 satisfies 型 の形で書きます。
type Config = Record<string, number | string>;
const config = {
port: 8080,
host: "localhost",
} satisfies Config;
// hoverで確認できる推論型:
// const config: { port: number; host: string; }
config.port; // number として扱える
ここで config は Config(Record<string, number | string>)ではなく、{ port: number; host: string } という具体的な型として推論されます。もし config に Config を型注釈で付けていたら、config.port は number | string に広がってしまいますが、satisfies ならキーごとの具体型がそのまま保たれるわけです。
リテラル型を保ちたいケースでも効果がわかりやすく出ます。
const palette = {
primary: "#FF0000",
secondary: "#00FF00",
} satisfies Record<string, string>;
// 推論型:{ primary: string; secondary: string; }
// primary / secondary というキーが存在することは保持される
palette.primary; // OK・補完も効く
palette.tertiary; // エラー:存在しないプロパティ
Record<string, string> で「文字列をキーに文字列を持つ」という制約を課しながらも、primary や secondary といった実在するキーの情報は推論型に残るため、存在しないキーへのアクセスはきちんと弾かれます。これが「検査はするが型は奪わない」の正体です。
基本構文と導入バージョン(4.9以降)
構文はシンプルで、式の末尾に satisfies 型 を付けるだけです。
const a = expr satisfies SomeType;
const b = { /* ... */ } satisfies SomeType;
const c = [1, 2, 3] satisfies readonly number[];
この演算子は TypeScript 4.9 で導入された機能です。公式リリースノートでも次のように説明されています。
TypeScript developers are often faced with a dilemma: we want to ensure that some expression matches some type, but also want to keep the most specific type of that expression for inference purposes.
出典:TypeScript Release Notes「TypeScript 4.9」The satisfies Operator
導入は 4.9 以降のため、それより前のバージョンでは構文エラーになります。プロジェクトの TypeScript が古い場合は、satisfies を書く前にバージョンを上げる必要があります。
なぜ生まれたか:型注釈とasの“穴”
satisfies が必要になった理由は、型注釈(:)と as のどちらにも「片方ずつ穴がある」からです。同じ値に対して両者を並べると、それぞれの欠点が見えてきます。
type Color = { r: number; g: number; b: number };
// 型注釈:検査は効くが、推論が Color に固定され情報が消える
const c1: Color = { r: 255, g: 0, b: 0 };
// c1 は Color。これ以上の具体情報はない
// as:型をアサーション先に置き換え、検査を弱める
const c2 = { r: 255, g: 0 } as Color;
// b が無いのにエラーにならない(検査を握りつぶしている)
c2.b; // 実行時は undefined になりうる
型注釈は安全ですが、推論された具体型を捨ててしまいます。一方 as は値を保てるものの、誤った値すら通してしまうため安全網になりません。この「検査か、推論か」の二択を強いられていた状況を解消するために satisfies が登場しました。
satisfiesと他の型指定の違いを比較表で整理
三者の違いは、「型エラーを検査するか」「推論された具体型を保つか」「コンパイラの判断を上書きできるか」の3軸で整理すると一望できます。
| 観点 | 型注釈 : |
as(型アサーション) |
satisfies |
|---|---|---|---|
| 型エラーの検査 | あり | 弱い(代入チェックを回避できるが、重ならない型は不可) | あり |
| 推論型の保持 | なし(型に固定) | なし(アサーション先の型に置き換わる) | あり |
| コンパイラ判断の上書き | しない | する | しない |
| 実行時の影響 | なし | なし | なし |
| 代表的な用途 | 変数の型を宣言する | 推論を意図的に矯正する | 検査しつつ具体型を残す |
ポイントは、satisfies だけが「検査あり」と「推論型の保持あり」を両立している点です。同じオブジェクトに三者を適用して、推論型がどう変わるかを並べると差が明確になります。
type Route = { path: string; method: string };
const a: Route = { path: "/", method: "GET" };
// 推論型:Route(path も method も string に広がる)
const b = { path: "/", method: "GET" } as Route;
// 推論型:Route(型が Route に置き換わる)
// もし { path: "/" } as Route のように不足があっても検査されず通ってしまう
const c = { path: "/", method: "GET" } satisfies Route;
// 推論型:{ path: string; method: string }
// Route への適合は検査されつつ、具体型は保たれる
型注釈(:)との違い:具体型が失われる
型注釈との決定的な差は、: は変数の型を指定した型そのものに固定し、キー単位の具体値情報を消してしまう点です。
const themes: Record<string, string> = {
light: "white",
dark: "black",
};
themes.light; // string
themes.unknown; // string(存在しないキーもエラーにならない)
型注釈で Record<string, string> を付けると、themes は「任意の文字列キーを持つ」型として扱われ、light や dark という実在キーの情報が失われます。存在しないキーへのアクセスすら通ってしまうわけです。
const themes = {
light: "white",
dark: "black",
} satisfies Record<string, string>;
themes.light; // string・補完が効く
themes.unknown; // エラー:存在しないプロパティ
satisfies に変えると、Record<string, string> への適合は検査しつつ、light / dark というキーの情報が残ります。「制約は課したいが、補完や具体型は残したい」場面では型注釈より satisfies が向くということです。
型アサーションとの違い:検査を上書きできる
as との決定的な違いは、as はコンパイラの検査を握りつぶし、誤った値でも通してしまう点にあります。
type User = { id: number; name: string };
const u = { id: 1 } as User;
// name が無いのにエラーにならない
console.log(u.name.toUpperCase()); // 実行時に TypeError
as は「この値はこの型だ」とコンパイラに言い切らせる演算子なので、不足など一部の不整合を通せるが、重ならない型への単発アサーションはエラーになります。同じ誤りを satisfies で書くと、その場で弾かれます。
const u = { id: 1 } satisfies User;
// 例:TS1360 Type '{ id: number; }' does not satisfy the expected type 'User'.
// Property 'name' is missing in type '{ id: number; }' but required in type 'User'.
// (TypeScriptバージョンにより文言が異なる場合あり)
値の型が合わない場合も同様にエラーになります。
const u = { id: "1", name: "Ann" } satisfies User;
// 例:TS2322 Type 'string' is not assignable to type 'number'.
// (TypeScriptバージョンにより文言が異なる場合あり)
つまり as が「最終手段としてコンパイラの判断を上書きする」道具なのに対し、satisfies は判断を上書きせず、誤りを正面から検査する道具です。
as(型アサーション)はどんな場面で使ってよく、どこに危険があるのか。使い所と注意点をまとめた解説です。
as const satisfies|widening防止と検査を同時に行う定番パターン
as const satisfies T は、as const でリテラル化・readonly 化してから、satisfies T で構造を検査するという二段構えのパターンです。順序には意味があり、先に as const で型を確定させ、その確定した型を satisfies で検査します。
まず widening(型の広がり)を確認します。オブジェクトリテラルは何もしないと、各プロパティの型が広い型へ推論されます。
const env1 = { mode: "production" };
// 推論型:{ mode: string }
// "production" というリテラルが string に広がっている(widening)
as const を付けると、この widening を抑えてリテラル型と readonly を確定できます。
const env2 = { mode: "production" } as const;
// 推論型:{ readonly mode: "production" }
ここに satisfies を重ねると、リテラル型・readonly を保ったまま、型への適合も検査されるという両取りが実現します。
type Env = { mode: "production" | "development" };
const env = { mode: "production" } as const satisfies Env;
// 推論型:{ readonly mode: "production" }
// Env への適合は検査されつつ、リテラル型が保持される
const bad = { mode: "prod" } as const satisfies Env;
// 例:TS2322 Type '"prod"' is not assignable to type
// '"production" | "development"'.
// (TypeScriptバージョンにより文言が異なる場合あり)
順序を逆にして satisfies Env as const のように書くと意図どおりに働きません。必ず as const を先に置き、リテラル化した値を satisfies で検査するのが正しい形です。
as const でリテラル型を固定する仕組みと、widening を抑える考え方を基礎から整理した解説です。
よくある適用例:設定オブジェクト・定数のexport
実務で as const satisfies が活きるのは、設定オブジェクトやルーティング定数を「型で縛りつつ、キーの補完とリテラル型は残したい」場面です。
type RouteConfig = Record<string, { path: string; auth: boolean }>;
export const routes = {
home: { path: "/", auth: false },
dashboard: { path: "/dashboard", auth: true },
} as const satisfies RouteConfig;
// routes.home.path は "/" というリテラル型
// routes.unknown はエラー(存在しないキー)
// 各エントリが RouteConfig の形に合うか検査される
RouteConfig で各値の構造を強制しつつ、home / dashboard というキーや "/" というパスのリテラル型がそのまま残ります。これにより、定数を export した先でもキー名の補完が効き、タイプミスは型エラーで気づけるという安全さと利便性を同時に得られます。
いつsatisfiesを使う?判断フローと注意点
三者の選択は、「推論された具体型を後で使うか」を起点に判断すると迷いません。
- 値の具体型を後で使う(補完・リテラル型を残したい)→
satisfies(必要ならas constを併用) - 単に変数の型を宣言したいだけ → 型注釈
: - コンパイラの判断を上書きしたい(最終手段)→
as
このうち日常的に選ぶべき第一候補は satisfies です。型注釈は型を固定したいときに、as はやむを得ず推論を矯正するときに限って使う、と位置づけると整理できます。
注意点として最も重要なのは、satisfies は型レベルでしか働かず、実行時には何もしないことです。satisfies を含むコードはコンパイル後に消えるため、外部から来る JSON やフォーム入力のように実行時に値の正しさを保証したい場合は、別途バリデーションが必要です。
// satisfies はコンパイル時の検査のみ
const data = JSON.parse(input) satisfies User;
// 実行時に input が User である保証はない
実行時の値検証まで担保したいときは、スキーマ検証ライブラリと組み合わせるのが定石です。
そのほか、satisfies は 4.9 以降でしか使えない点と、有限のキー集合を持つ型にオブジェクトリテラルを直接 satisfies させる場合は、型にないキー(過剰なプロパティ)を検出できる点も押さえておくとよいですよ。
よくある質問
Q1. satisfiesと型注釈(:)はどちらを使うべき?
推論された具体型を後段で使うなら satisfies を選びます。キー名の補完やリテラル型を保ったまま、型への適合だけ検査できるためです。一方、単に変数の型を宣言したいだけで具体型が不要なら、型注釈 : で十分です。「具体型を残したいか」が分かれ目になります。
Q2. satisfiesは実行時に型チェックしてくれる?
いいえ、satisfies は型レベルの検査のみで、コンパイル後のコードには残りません。実行時には何の検証も行わないため、外部入力など実行時に値の正しさを保証したい場合は、zod などのスキーマ検証ライブラリを使う必要があります。
Q3. satisfiesが使えない(エラーになる)のはなぜ?
satisfies は TypeScript 4.9 で導入されたため、4.9 未満では構文エラーになります。使えないときは、まず利用中の TypeScript バージョンを確認してください。バージョンが古ければアップデートで解決します。
Q4. as const satisfies と satisfies as const のどちらが正しい?
as const satisfies T の順が正しい書き方です。先に as const でリテラル化・readonly 化して型を確定させ、その確定した型を satisfies T が検査するという流れになります。逆順では意図どおりに働きません。
【付録】さらに学びを深めるためのリソース
さらに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プログラマーになれるはずです。
まとめ – satisfiesは検査するが型は奪わない
この記事では、satisfies 演算子と型注釈・as の使い分けを整理しました。
satisfiesは値を型で検査するが、推論された具体型は奪わない:は具体型を奪い、asは検査を奪う——この対比が選択の軸- 具体型を後で使うなら
satisfies、宣言だけなら:、上書きが要るなら最終手段のas as const satisfies Tは widening 防止と検査を両立する定番パターン(順序はas constが先)satisfiesは型レベルのみで、実行時には何もしない
「検査はするが型は奪わない」という一点を覚えておけば、設定オブジェクトや定数を安全かつ便利に縛れるようになります。
as(型アサーション)の使い所と危険性を、具体例とともに整理した解説です。satisfies との対比をさらに深めたい方はこちら。
as const とリテラル型・widening の仕組みを基礎から押さえると、as const satisfies の理解が一段深まります。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。






