型アサーションと型ガード — as・is・as constの使い分け
この章の目次開く
- 型アサーション(as)
- もう1つの記法 値
- どういう時に使用するか
- 型アサーションは最後の手段
- 型アノテーションと型アサーションの違い
- 型アサーションと型キャストの違い
- 非nullアサーション(!)
- 「!」は実行時の安全を保証しない
- 「!」は TypeScript 専用、「?.」「??」は JavaScript の機能
- 型ガードで型安全を保証する
- ユーザー定義型ガード(is)
- 「嘘をつく型ガード」に注意
- TypeScript 5.5からは「型述語の推論」が効く
- ダブルアサーション(as unknown as T)の罠
- AI生成コードでよく見るパターン
- constアサーション(as const)
- 配列からユニオン型を導出する定番パターン
- as constは「安全なアサーション」
- 型アサーション vs 型ガード — どちらを使うべきか
- ちゃんと使うためのポイント
- 参考リンク
05章で学んだNarrowingは、TypeScriptが制御フローから自動的に型を絞り込む仕組みでした。この章では、開発者が明示的に型を指定する手法を扱います。
学習者コンパイラが「型が違う」って怒るとき、とりあえず as で黙らせちゃダメなの…?
型アサーション(as)
型アサーションは、TypeScriptの型推論結果を開発者が上書きする手段です。
構文: 値 as 型
// getElementById の戻り値は HTMLElement | null
const input = document.getElementById('my-input') as HTMLInputElement;
// HTMLInputElement として扱えるので、input 固有のプロパティが使える
console.log(input.value);as を書いた瞬間、コンパイラは推論をやめて「開発者の申告」を信じます。データそのものは何も変わらず、型の見え方だけが変わる点がポイントです。
どういう時に使用するか
具体的な使用シーンは主に以下の3つです。
| 使用シーン | 説明 |
|---|---|
| DOM要素を取得する時 | document.getElementById('my-input') などで要素を取得すると、TypeScriptはそれを単なる「一般的なHTML要素」としか認識しません。HTMLInputElement など具体型に断言することで、.value など要素固有のプロパティがエラーにならずに使えます。 |
| 外部データ(APIのレスポンスなど)を受け取る時 | JSON.parse() の結果など、外部から来たデータは中身がわからないため any や unknown 型になります。「このデータは User 型だ」と断言して、プログラム内で扱いやすくします。 |
| イベントハンドラの引数を使う時 | クリック時などの event.target は抽象的な型になっているため、ボタンやリンクなど具体的なHTML要素の型に断言して操作します。 |
コードで書くと次のようになります。
// ① DOM要素を取得する時
const input = document.getElementById('email') as HTMLInputElement;
input.value = 'test@example.com';
// ② 外部データ(APIのレスポンスなど)を受け取る時
type User = { id: number; name: string };
const user = JSON.parse(jsonText) as User;
console.log(user.name);
// ③ イベントハンドラの引数を使う時
document.querySelector('button')?.addEventListener('click', (event) => {
const target = event.target as HTMLButtonElement;
target.disabled = true;
});型アサーションは「コンパイラの安全装置をオフにして無理やり通す」行為です。そのため、本当にその型である確証がある時だけ使う(乱用しない)のが鉄則です。
型アノテーションと型アサーションの違い
型アノテーション と 型アサーション は、
-
型アノテーション『注釈』
変数や関数を定義する際に、「これにはこの型のデータが入る」と最初から明示的に指定するもの
-
型アサーション『断定』
コンパイラが自動で推論した型に対して、プログラマが「いいや、これはこの型として扱え」と強制的に上書きするものです
どちらも型を指定する役割を持ちますが、使われる目的やタイミングに違いがあります。
// 型アノテーション:定義時に「この型が入る」と宣言する
// 中身が User と合っていなければ、その場でコンパイルエラーになる
const user1: User = { id: 1, name: 'Tanaka' };
// 型アサーション:推論結果を「この型として扱え」と上書きする
// 中身が本当に User かどうかはチェックされない
const user2 = JSON.parse(jsonText) as User;型アサーションと型キャストの違い
型キャスト は別の型に変換する手段です。
一方、型アサーション はデータそのものは一切変更しません。TypeScriptのコンパイラに対する「見せ方(認識)」だけを変えます。
下記は型キャストの例
const n = 123; // nは数値(Number)
const s = String(n); // sは文字列(String)の "123" に変換される非nullアサーション(!)
非nullアサーションは、変数の後ろに ! を付けることで「この値は null や undefined ではない」とコンパイラに断言する記法です。
値が null または undefined になりうる型(例:string | null)に対して、開発者が「ここでは絶対に値が存在する」と保証することで、null チェックなしでプロパティやメソッドにアクセスできるようになります。
function getLength(text: string | null) {
// ① text は null の可能性があるため、そのまま .length にアクセスするとエラー
// console.log(text.length); // ❌ 'text' is possibly 'null'.
// ② ! を付けて「null ではない」と断言すると、エラーにならずアクセスできる
console.log(text!.length);
}よく使われるのは、DOM要素の取得結果のように「存在するはずだが、型の上では null の可能性が残る」ケースです。
// getElementById の戻り値は HTMLElement | null
const button = document.getElementById('submit')!;
// ! を付けることで HTMLElement として扱える
button.addEventListener('click', () => {});そのため、可能な限り ! ではなく、より安全な代替手段を優先します。
| 手段 | 例 | 特徴 |
|---|---|---|
| オプショナルチェーン(?.) | text?.length | null / undefined なら処理を中断し undefined を返す |
| Null合体演算子(??) | text ?? '初期値' | null / undefined のときに既定値を使う |
| 型ガード(if文によるチェック) | if (text !== null) { ... } | 実行時に確認するため最も安全 |
! は「他に手段がなく、かつ値が存在することが確実に分かっている」場合の最後の手段として使うのが鉄則です。
型ガードで型安全を保証する
型ガード(Type Guard)とは、 プログラムの実行時にデータの中身をチェックすることで、TypeScriptの型を安全に絞り込む仕組みです。
function printValue(value: string | number) {
// ① この時点では string か number か分からないため、文字列専用の value.length はエラーになる
if (typeof value === 'string') {
// ② 型ガードが働き、このブロック内では確実に「string(文字列)」として認識される!
console.log(value.length); // エラーにならない
} else {
// ③ ここでは自動的に残りの「number(数値)」として認識される
console.log(value * 10);
}
}typeof のほか、instanceof や in を使った基本の絞り込みは05章の型の絞り込み(Narrowing)で扱いました。ここでは、その絞り込みロジックを再利用可能な関数として切り出す方法を見ていきます。
ユーザー定義型ガード(is)
同じ判定ロジックをあちこちの if 文に書くのは冗長です。そこで判定処理を関数に切り出し、戻り値の型の位置に「型述語(type predicate)」を書くことで、呼び出し元でも型の絞り込みが効くようにしたものが、ユーザー定義型ガードです。
構文: function 関数名(引数名: 型): 引数名 is 絞り込み後の型
| 部分 | 説明 |
|---|---|
引数名: 型 | チェックしたい値。外部データの検証では unknown で受けることが多い |
引数名 is 絞り込み後の型 | 戻り値の型の位置に書く型述語。この関数が true を返した場合、呼び出し元でその引数が指定した型に絞り込まれる |
戻り値: boolean(true = その型である / false = 違う)
典型例は、APIレスポンスのような unknown なデータを検証してから使うパターンです。
type User = {
id: number;
name: string;
};
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
typeof value.id === 'number' &&
'name' in value &&
typeof value.name === 'string'
);
}この isUser を通すと、if ブロックの中では unknown が User として扱えます。
async function fetchUser(): Promise<User | null> {
const res = await fetch('/api/user');
const data: unknown = await res.json();
if (isUser(data)) {
return data; // ✅ このブロック内では data は User 型
}
return null; // 検証に失敗したデータは通さない
}
学習者結局「これは User だ」って自分で申告してるんだから、as User と同じじゃないの…?
決定的な違いは、実行時チェックの有無です。as は中身を一切確認せずに型だけを上書きしますが、is を使った型ガードは「実行時に中身を検査し、合格したものだけを通す」仕組みです。as は「ノーチェックの自己申告」、is は「実行時検査に合格したことをコンパイラに伝える契約」です。

型述語は、配列の filter で「絞り込んだ結果の配列」を得たいときにも使えます。
const values = ['a', null, 'b', null]; // (string | null)[]
// 型述語をアロー関数の戻り値に書くと、結果が string[] になる
const strings = values.filter((v): v is string => v !== null);なお、true / false を返す代わりに「不正なら throw する」形で切り出すアサーション関数(asserts)もあります。こちらは05章のアサーション関数パターンで扱いました。
ダブルアサーション(as unknown as T)の罠
学習者as any がダメなら as unknown as にすれば安全ですよね? AIに「anyを使わないで」って指示したら、こっちに書き換えてくれたんですけど……
結論から言うと、as unknown as T を書いている時点で型安全性は放棄しています。as any as T と本質的な危険度は大差ありません。
// どちらも「型チェックを黙らせる」行為
const a = value as any as TargetType;
const b = value as unknown as TargetType;TypeScriptは通常、互換性のない型同士のアサーションをエラーにしてくれます。
const n: number = 42;
const s = n as string; // ❌ エラー:number と string は互換性がないダブルアサーションは、一度 any や unknown を経由することでこの安全装置を迂回します。unknown 経由のほうが @typescript-eslint/no-explicit-any ルールに引っかからないぶん行儀が良く見えますが、やっていることは同じです。
ダブルアサーションが出てきたら、まず設計を見直します。
| 状況 | 代わりにやること |
|---|---|
| APIレスポンスの型が合わない | ジェネリクスで型パラメータを渡す / Zodなどでバリデーション |
| ライブラリの型定義が古い | @types を更新する / 型定義を拡張(declaration merging) |
| テストでモックを作りたい | Partial<T> や satisfies を活用する |
| 本当にどうしようもない | as unknown as T を使い、コメントで理由を残す |
先生as unknown as に統一すること自体は悪くありません。lintルールと整合しますし、コードレビューで「ここは意図的にキャストしている」と分かりやすくなります。ただ、そもそもダブルアサーションが必要な設計自体を見直すほうが先、という順番が大事です。
constアサーション(as const)
as const は名前こそ「アサーション」ですが、これまでの as T とは性格が正反対です。型を偽るのではなく、推論される型をできるだけ狭く・固定的にするための機能で、安全に使えます。
まず、as const なしの推論結果を見てみます。
const config = {
endpoint: '/api/users',
method: 'GET',
};
// 推論結果: { endpoint: string; method: string }method の値は 'GET' と書いたのに、型は string に**広げられて(widening)**推論されます。オブジェクトのプロパティは後から書き換えられるため、TypeScriptは安全側に倒して広い型を付けるのです。これが困る場面があります。
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
function request(url: string, method: HttpMethod) {
/* ... */
}
request(config.endpoint, config.method);
// ❌ エラー:string は HttpMethod に代入できないas const を付けると、推論結果がリテラル型のまま固定されます。
const config = {
endpoint: '/api/users',
method: 'GET',
} as const;
// { readonly endpoint: "/api/users"; readonly method: "GET" }
request(config.endpoint, config.method); // ✅ "GET" は HttpMethod に代入できるas const の効果は次の3つです。
| 効果 | 説明 |
|---|---|
| リテラル型に固定 | 'GET' が string に広げられず、"GET" 型のまま保たれる |
すべて readonly になる | プロパティへの再代入がコンパイルエラーになる(ネストしたオブジェクトも) |
| 配列が読み取り専用タプルになる | [10, 20] が number[] ではなく readonly [10, 20] になる |
配列からユニオン型を導出する定番パターン
as const の実務での代表的な使いどころが、「選択肢の配列」と「その要素のユニオン型」を1つの定義から導出するパターンです。
const CATEGORIES = ['tech', 'design', 'business'] as const;
// 型: readonly ["tech", "design", "business"]
type Category = (typeof CATEGORIES)[number];
// 型: "tech" | "design" | "business"
// 実行時は配列として使える
CATEGORIES.map((c) => console.log(c));
// 型の世界ではユニオン型として使える
function setCategory(category: Category) {
/* ... */
}型アサーション vs 型ガード — どちらを使うべきか
| 手法 | 安全性 | 使う場面 |
|---|---|---|
型ガード(typeof, in, ユーザー定義) | 高い | ランタイムで検証できる場合 |
型アサーション(as T) | 低い | 型が正しいと確信できる場合 |
ダブルアサーション(as unknown as T) | 最低 | テストやモックなどやむを得ない場面のみ |

基本方針:まず型ガードで解決できないか考える。 as は「TypeScriptが推論できないが、自分には型が分かっている」場面の最終手段として使います。
ちゃんと使うためのポイント
- 型ガードはランタイムで検証するので安全。型アサーションは自己申告なので危険
as anyもas unknown as Tも型安全性を放棄している点は同じas constはアサーションではなくリテラル型への固定。安全に使える- ユーザー定義型ガード(
is)はAPIレスポンスのバリデーションで特に有用。ただし検査ロジックの正しさは自分で保証する - TypeScript 5.5以降、単純な関数の型述語は自動推論される(特定の型へ絞る場合は明示的に
isを書く)
次の章では、Partial や Pick など、既存の型から新しい型を作り出すユーティリティ型を学びます。この章の表に登場した「モックには Partial<T> を使う」の正体も、そこで分かります。
参考リンク
- TypeScript Handbook — Everyday Types(英語) — Type Assertions の節に公式の説明がある
- TypeScript Handbook — Narrowing(英語) — Using type predicates の節にユーザー定義型ガードの公式説明がある
- TypeScript 3.4 リリースノート(英語) — const assertions が導入されたバージョンの公式解説
