06. any・unknown・型アサーション

型検査から降りる3つの方法と、その代償

📅 作成: 2026-08-29 / 更新: 2026-08-29

この章の到達点

この章の内容

  1. any — 伝播する
  2. unknown — 安全な受け口
  3. as — 検査を黙らせる
  4. ! — 非 null アサーション

06.1 any — 伝播する

any は「何でも入る」ではなく「検査しない」

any を付けた値には、どんな操作をしてもエラーになりません。

const x: any = "文字列";

x.toFixed(2);          // エラーにならない(実行時に落ちる)
x.foo.bar.baz;         // エラーにならない(実行時に落ちる)
x();                   // エラーにならない(実行時に落ちる)

下流に広がる

問題は、any が伝染することです。

function parse(json: string): any {
	return JSON.parse(json);
}

const user = parse('{"name":"Alice"}');   // user は any
const name = user.name;                   // name も any
const upper = name.toUpperCase();          // upper も any
const len = upper.lenght;                  // 打ち間違いも通る(実行時 undefined)

1か所の any が、その先すべての検査を無効化します。

上の例では lenght という打ち間違いが検出されません。any を返す関数を1つ書くと、それを使うコード全体が型のない世界になります。

any で受けた場合 — 下流すべてに広がる JSON.parse 外部データ any user.name any .lenght any 打ち間違いも 通ってしまう unknown で受けた場合 — 確かめるまで先へ進めない JSON.parse 外部データ unknown 型ガードで確かめる isUser(v) User 型が付いた値 検査が効く
図 1 — any は下流すべてに広がり、unknown は絞り込みを強制する

暗黙の any

strict 型を書き忘れた引数は、暗黙に any になります。strict が有効ならエラーで教えてくれます。

function greet(name) {
	//         ~~~~
	// error TS7006: Parameter 'name' implicitly has an 'any' type.
	//
	// 訳: 引数 'name' は暗黙的に 'any' 型になっています。
	return "Hello, " + name;
}

03. tsconfig のとおり、TypeScript 7 では strict が既定で有効です。5 系から移行すると、このエラーが大量に出ることがあります。

any が要る場面はほとんどない

やりたいこと使うもの
外部から来た値を受けるunknown(06.2)
どんな型でも受ける関数を書くジェネリクス09
オブジェクトなら何でも受けるRecord<string, unknown>
型定義のないライブラリを使う自分で .d.ts を書く(10
とりあえず動かしたいany でよい。ただし後で消す前提で、コメントを残す

06.2 unknown — 安全な受け口

「何でも入るが、確かめるまで何もできない」

unknownany と同じくどんな値でも代入できます。違うのは使うときに絞り込みを強制される点です。

const x: unknown = "文字列";

x.toUpperCase();
// ~
// error TS18046: 'x' is of type 'unknown'.
//
// 訳: 'x' は 'unknown' 型です。

if (typeof x === "string") {
	x.toUpperCase();      // ここでは string として扱える
}
項目anyunknown
どんな値でも代入できるできるできる
確かめずに使える使える(危険)使えない(安全)
他の型に代入できるできる(危険)できない
伝播するするしない

外部から来る値は unknown で受ける

JSON.parse の戻り値は any です。受け口で unknown に変えると、検証を強制できます。

// unknown-parse.ts
function parseJson(text: string): unknown {
	return JSON.parse(text);
}

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

function isUser(v: unknown): v is User {
	return (
		typeof v === "object" && v !== null &&
		typeof (v as Record<string, unknown>).name === "string" &&
		typeof (v as Record<string, unknown>).age === "number"
	);
}

const data = parseJson('{"name":"Alice","age":30}');

if (isUser(data)) {
	console.log(`${data.name} は ${data.age} 歳`);
} else {
	console.log("User の形ではありません");
}

console.log(isUser(parseJson('{"name":"Bob"}')));
$ npx tsx unknown-parse.ts
Alice は 30 歳
false

v is User という書き方は型ガード関数です。詳細は 08. 型の絞り込み で扱います。

実務ではライブラリを使ってください。

上のような検証を手で書くと、プロパティが増えるたびに書き足すことになります。zod・valibot のようなスキーマ検証ライブラリを使うと、検証と型定義を1か所で書けます。この資料では仕組みを理解するために手書きしています。実務での書き方は A3. 実務パターン集 に置いています。

06.3 as — 検査を黙らせる

as は変換ではない

as型アサーションです。「この値はこの型だと思って扱え」とコンパイラに指示するだけで、値は何も変わりません。

const x: unknown = "文字列";
const n = x as number;        // 通る。何も起きない

console.log(n.toFixed(2));    // 実行時エラー: n.toFixed is not a function

Java・C# から来た人へ

Java の (Integer) obj や C# の (int)obj実行時に検査され、失敗すれば例外になります。TypeScript の as実行時に何もしません。型が消えている以上、検査するものがないためです(01. 背景)。

C# の as 演算子(失敗時に null)とも違います。TypeScript の as は、検査を黙らせるだけです。

as が嘘をつく瞬間

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

const data = JSON.parse('{"name":"Alice"}') as User;

console.log(data.age.toFixed(0));
// 型の上では number なので通る
// 実行時: TypeError: Cannot read properties of undefined (reading 'toFixed')

外部から来た値に as を使うのが最も危険です。実際のデータが型と違っていても、誰も教えてくれません。

型の世界(開発中だけ存在する) 値の世界(実行時に存在する) unknown User 「User だと思え」 as User 検査が黙る data.age が number に見える { name: "Alice" } age は存在しない { name: "Alice" } 何も変わっていない 値は素通り 実行時に落ちる Cannot read properties of undefined
図 2 — as は型の世界だけを書き換える。値は何も変わらない

as を使ってよい場面

判定場面理由
よい検証を済ませた直後(ブランド型の生成関数など)人間が保証した直後で、範囲が狭い
よいas constこれは別物。リテラル型として固定する指示(04
条件付きDOM の要素取得(as HTMLInputElement型定義側が絞れないだけ。実物と一致するか確認する
避ける外部から来たデータ(JSON・API・環境変数)検証していない。unknown と型ガードを使う
避けるエラーを消すためエラーの原因が残ったままになる

二段階の as は危険信号

const s = "文字列";
const n = s as unknown as number;      // 無関係な型にも変換できてしまう

as近い型どうしでしか使えませんが、as unknown as を挟むと何にでも変換できます。この書き方が出てきたら、設計を見直す合図です。

satisfies — 型を確かめるが、型は変えない

as の代わりに使える演算子です。「この型を満たしているか確かめるが、推論された型はそのまま残す」という意味です。

type Config = Record<string, string | number>;

// as だと、推論された具体的な型が失われる
const a = { host: "localhost", port: 3000 } as Config;
// a.port の型は string | number

// satisfies なら、検査もされるし具体的な型も残る
const b = { host: "localhost", port: 3000 } satisfies Config;
// b.port の型は number

設定オブジェクトを書くときは satisfies が適しています。誤った値を検出しつつ、使う側では具体的な型を保てます。

06.4 ! — 非 null アサーション

「null ではない」と言い切る

function find(id: string): User | undefined { /* ... */ }

const user = find("1");
console.log(user.name);
//          ~~~~
// error TS18048: 'user' is possibly 'undefined'.

console.log(user!.name);     // ! を付けると通る(検査されない)

!asnull 版です。実行時には何も起きません。間違っていれば TypeError になります。

使ってよい場面・いけない場面

判定代わりに使うもの
避ける検索結果が見つかる前提のときif (!user) throw new Error(...) で明示的に落とす
避ける環境変数(process.env.PORT!読み込み時に検証する(A3
条件付き直前に存在を確認済みだが、型が追従しないときできれば絞り込みで解決する(08
よいクラスのプロパティ定義(name!: string初期化が別の場所で確実に行われる場合のみ

! の代わりに例外を投げる

// assert.ts
function findUser(id: string): { name: string } | undefined {
	const db: Record<string, { name: string }> = { "1": { name: "Alice" } };
	return db[id];
}

// ! を使わず、見つからないことを明示的に扱う
function getUserOrThrow(id: string): { name: string } {
	const user = findUser(id);
	if (!user) {
		throw new Error(`ユーザーが見つかりません: ${id}`);
	}
	return user;      // ここでは undefined でないことが型でも分かる
}

console.log(getUserOrThrow("1").name);

try {
	getUserOrThrow("999");
} catch (e) {
	console.log((e as Error).message);
}
$ npx tsx assert.ts
Alice
ユーザーが見つかりません: 999

どちらも実行時に落ちますが、落ち方が違います。

! を使うと TypeError: Cannot read properties of undefined という原因の分からないエラーになります。明示的に投げれば「何が見つからなかったか」が分かります。デバッグにかかる時間が変わります。