0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TypeScriptのsatisfies・型注釈・asを、推論結果と欠落チェックで比べる

0
Last updated at Posted at 2026-10-22

satisfiesは、設定値が型を満たすか確かめながら、各プロパティの具体的な型を使いたいときに役立ちます。ただし「何を付けても推論は一切変わらない」「余分なキーを必ず拒否する」と覚えると、実際の診断と食い違います。

同じオブジェクトを型注釈、satisfies、asで宣言し、生成された.d.tsとエラーを比べました。結論を先に置くと、今回の表示設定では次の違いがありました。

書き方 readyを読むときの型 waitingの欠落
: Notices `string number[]`
satisfies Notices string エラー
as Notices `string number[]`

推論は補完表示だけで判断しない

検証環境は2026年9月11日、macOS arm64、Node.js 24.15.0、TypeScript 7.0.2です。strict: true、target: ES2022、module: ESNext、moduleResolution: Bundlerで、型検査とdeclaration出力を実行しました。値は比較用に作った表示設定です。

type Notices = Record<"ready" | "waiting", string | number[]>;
export const annotated: Notices = { ready: "Ready", waiting: [1, 2] };
export const checked = { ready: "Ready", waiting: [1, 2] } satisfies Notices;
export const asserted = { ready: "Ready", waiting: [1, 2] } as Notices;
console.log(checked.ready.toUpperCase());

checked.ready.toUpperCase()は通り、実行結果はREADYでした。annotated.readyへ同じメソッド呼び出しを書くと、配列の可能性が残るためTS2339です。

生成された宣言を見ると、呼び出し可否の理由が分かります。

type Notices = Record<"ready" | "waiting", string | number[]>;
export declare const annotated: Notices;
export declare const checked: {
    ready: string;
    waiting: number[];
};
export declare const asserted: Notices;
export {};

checkedには各プロパティの型が残り、他の2つはNoticesという注釈の型になります。satisfiesなら許容する値の種類をまとめて検査し、個別の値を使う箇所で余分な絞り込みをせずに済みます。TypeScript 4.9の説明でも、式の型を扱いやすいまま適合性を調べる用途が示されています。

asを検査の代わりにすると欠落が残る

type Notices = Record<"ready" | "waiting", string | number[]>;
const value = { ready: "Ready" } as Notices;
console.log(value.waiting);

このコードは型検査を通り、undefinedを出力しました。同じ値をsatisfies Noticesへ変えると、waiting欠落のTS2741になります。asでプロパティが作られるわけではありません。

一方、asなら任意の変換が全部通る、という意味でもありません。TypeScriptは型同士の関係によってアサーションを拒否します。ここで確認したのは、必須キーの欠落があるこの具体例を通すことです。型アサーションの公式説明にあるとおり、実行時の値の検査や変換にはなりません。

「推論を変えない」には文脈がある

export const plain = { enabled: true, count: 1 };
export const checked = { enabled: true, count: 1 } satisfies { enabled: boolean; count: number };
export const frozenType = { enabled: true, count: 1 } as const;

この宣言出力では、plain.enabledはboolean、checked.enabledはtrueでした。countはどちらもnumberです。対象型から受ける文脈によって、リテラルの扱いまで完全に同一とは限りません。

as constを付けたfrozenTypeでは、enabledがreadonlyなtrue、countがreadonlyな1になりました。これも実行時にオブジェクトを凍結する操作ではありません。設定をあとで書き換える設計なら、trueに狭まったプロパティへfalseを入れたいのか、最初からbooleanを受け入れたいのかを分けて考えます。

余分なキーを拒否する範囲

{ ready: "Ready", waiting: [1], typo: 1 } satisfies Noticesを直接書くと、余分なtypoにTS2353が出ました。ところが、その値を先に変数へ入れると結果が変わります。

type Notices = Record<"ready" | "waiting", string | number[]>;
const input = { ready: "Ready", waiting: [1], typo: 1 };
const value = input satisfies Notices;
console.log(value.typo);

こちらは通り、1を出力します。必要なプロパティを持つ値として適合しても、余分なプロパティを取り除きません。外部データのキーを制限したい場合は、型構文とは別に実行時の検査が必要です。

この実験で選ぶ基準は、ローカルな設定の誤りを検出し、値ごとの操作を残したいならsatisfies、変数に持たせる型を明示したいなら型注釈です。asは、その型だと判断できる別の根拠がある箇所に限って使います。

再現コードのT02ケースと実行記録には、成功した宣言と意図的に失敗させた入力の両方を残しました。診断番号や推論結果を別バージョンへそのまま一般化せず、更新時は宣言出力も比べてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?