お久しぶりです、白々さじきです。
最近TypeScriptを勉強することが多いので型の勉強を始めました。
私の学習のために生成AIに作らせた文章を共有します。
今回は、TypeScriptの unknown 型について解説します。「any は危険だから使うな」と言われて unknown を勧められたけど、使い方がよく分からない……という方も多いのではないでしょうか。
この記事で解決すること
unknown型とは何か、なぜ存在するのかが分かるanyとの違いを安全性・保守性の観点で理解できるunknownを安全に扱う手順(型ガード / narrowing / assertion)が身につく- 実務での利用シーン(APIレスポンス、catchのerror、JSON.parse等)が分かる
結論
unknown は「型が分からない値を安全に扱うための型」です。any と違い、型チェックなしでは操作できないため、バグの混入を防げます。外部から来るデータ(API、JSON、catchのerror等)には unknown を使い、型ガードで絞り込んでから使うのがベストプラクティスです。
unknown型とは?
unknown 型は TypeScript 3.0 で導入された型です。公式ドキュメントでは次のように説明されています。
The unknown type represents any value. This is similar to the any type, but is safer because it’s not legal to do anything with an unknown value.
(unknown型はあらゆる値を表します。any型に似ていますが、unknown値に対しては何も操作できないため、より安全です。)
要するに、「型が何か分からない」ことを明示的に表す型です。any が「何でもOK」なのに対し、unknown は「何か分からないから、確認してから使ってね」という意味になります。
anyとunknownの違い
anyの危険性
まず、any の問題点を見てみましょう。
悪い例:anyを使ったコード
// any型は何でも代入できる
let value: any = "hello";
// 型チェックなしで何でもできてしまう
console.log(value.toFixed(2)); // 実行時エラー! stringにtoFixedはない
console.log(value.foo.bar); // 実行時エラー! fooプロパティは存在しない
// 他の型にも代入できてしまう
const num: number = value; // コンパイルエラーにならない
any を使うと、TypeScriptの型チェックが完全に無効化されます。コンパイル時にはエラーが出ず、実行時に初めてバグが発覚するという最悪のパターンになります。
unknownの安全性
次に、unknown を使った場合を見てみましょう。
良い例:unknownを使ったコード
// unknown型も何でも代入できる
let value: unknown = "hello";
// しかし、型チェックなしでは操作できない
console.log(value.toFixed(2)); // コンパイルエラー!
console.log(value.foo.bar); // コンパイルエラー!
// 他の型にも代入できない
const num: number = value; // コンパイルエラー!
// 型を確認してから使う必要がある
if (typeof value === "string") {
console.log(value.toUpperCase()); // OK! stringとして扱える
}
unknown は「型チェックを強制する any」と考えると分かりやすいです。
両者の違いを表にまとめます。
| 比較項目 | any | unknown |
|---|---|---|
| 任意の値を代入 | ○ できる | ○ できる |
| プロパティアクセス | ○ できる(危険) | × できない |
| メソッド呼び出し | ○ できる(危険) | × できない |
| 他の型への代入 | ○ できる(危険) | × できない |
| 型安全性 | なし | あり |
unknownを安全に扱う方法
unknown 型の値を使うには、型を絞り込む(narrowing)必要があります。TypeScriptには複数の絞り込み方法があります。
参考:TypeScript Handbook: Narrowing
typeof による型ガード
プリミティブ型(string, number, boolean等)の判定に使います。
function processValue(value: unknown): string {
// typeof で型を絞り込む
if (typeof value === "string") {
return value.toUpperCase(); // string として扱える
}
if (typeof value === "number") {
return value.toFixed(2); // number として扱える
}
if (typeof value === "boolean") {
return value ? "true" : "false";
}
return "unknown type";
}
instanceof による型ガード
クラスのインスタンス判定に使います。
function processError(error: unknown): string {
// Error クラスのインスタンスか判定
if (error instanceof Error) {
return error.message; // Error として扱える
}
if (error instanceof TypeError) {
return `TypeError: ${error.message}`;
}
return "Unknown error";
}
in 演算子による型ガード
オブジェクトのプロパティ存在チェックに使います。
interface User {
name: string;
email: string;
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"name" in value &&
"email" in value
);
}
function greetUser(input: unknown): string {
if (isUser(input)) {
return `Hello, ${input.name}!`; // User として扱える
}
return "Hello, stranger!";
}
上記の value is User は「型述語(Type Predicate)」と呼ばれ、カスタム型ガード関数を作るときに使います。
型アサーション(最終手段)
型ガードが難しい場合の最終手段です。ただし、型安全性が失われるため慎重に使ってください。
悪い例:いきなり型アサーション
function processData(data: unknown) {
// 危険:型チェックなしでアサーション
const user = data as User;
console.log(user.name); // dataがUserでなければ実行時エラー
}
良い例:型ガード後に型アサーション
function processData(data: unknown) {
// 先に型ガードで検証
if (
typeof data === "object" &&
data !== null &&
"name" in data &&
typeof (data as { name: unknown }).name === "string"
) {
const user = data as User;
console.log(user.name); // 安全
}
}
実務での利用例
APIレスポンスの型付け
外部APIからのレスポンスは、実行時まで内容が保証されません。unknown で受け取り、型ガードで検証するのが安全です。
interface ApiResponse {
status: "success" | "error";
data?: unknown;
message?: string;
}
interface UserData {
id: number;
name: string;
}
function isUserData(value: unknown): value is UserData {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value &&
typeof (value as UserData).id === "number" &&
typeof (value as UserData).name === "string"
);
}
async function fetchUser(id: number): Promise<UserData | null> {
const response = await fetch(`/api/users/${id}`);
const json: unknown = await response.json();
// 型ガードで検証
if (isUserData(json)) {
return json;
}
console.error("Invalid response format");
return null;
}
try-catchのerror
TypeScript 4.4以降、catch節のerrorはデフォルトで unknown 型になりました(useUnknownInCatchVariables オプション)。
悪い例:errorをanyとして扱う
try {
throw "something went wrong";
} catch (error) {
// errorがError型とは限らない
console.log(error.message); // 実行時エラーの可能性
}
良い例:errorをunknownとして安全に処理
try {
throw "something went wrong";
} catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message);
} else if (typeof error === "string") {
console.log(error);
} else {
console.log("An unknown error occurred");
}
}
JSON.parseの結果
JSON.parse の戻り値は any ですが、unknown として扱うのが安全です。
function parseConfig(jsonString: string): Config | null {
try {
const parsed: unknown = JSON.parse(jsonString);
if (isConfig(parsed)) {
return parsed;
}
console.error("Invalid config format");
return null;
} catch {
console.error("Failed to parse JSON");
return null;
}
}
詰まりポイント / FAQ
Q1. unknownのまま使おうとしてエラーが出る
A. unknown は型を絞り込むまで操作できません。typeof、instanceof、カスタム型ガード等で型を確定させてから使ってください。
// NG: unknownのまま使用
function bad(value: unknown) {
return value.toString(); // エラー
}
// OK: 型ガード後に使用
function good(value: unknown) {
if (typeof value === "string" || typeof value === "number") {
return value.toString(); // OK
}
return String(value);
}
Q2. 面倒だから結局anyに逃げてしまう
A. 気持ちは分かりますが、型ガード関数を共通化すれば楽になります。プロジェクト共通の型ガードユーティリティを作っておくと、再利用できて便利です。
// utils/typeGuards.ts
export function isString(value: unknown): value is string {
return typeof value === "string";
}
export function isNumber(value: unknown): value is number {
return typeof value === "number";
}
export function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
Q3. unknownとneverの違いは?
A. unknown は「どんな型か分からない(全ての型の可能性がある)」、never は「どの型にも当てはまらない(値が存在しない)」です。集合論的に言うと、unknown は全体集合、never は空集合です。
Q4. anyを使っていい場面はある?
A. 以下の場合は許容されることがあります。
- 既存のJSコードをTSに移行中で、段階的に型付けしている場合
- サードパーティライブラリの型定義が不完全な場合(一時的な回避策として)
- プロトタイピング段階で、後で型を付ける前提の場合
ただし、本番コードでは極力 unknown + 型ガードを使うべきです。
まとめ
今回は、TypeScriptの unknown 型について解説しました。
unknownは「型が分からない値を安全に扱う型」anyと違い、型チェックなしでは操作できない- 型ガード(typeof / instanceof / in / カスタム型ガード)で絞り込んでから使う
- 外部入力(API、catch、JSON.parse等)には
unknownを使うのがベストプラクティス
any に逃げたくなる気持ちは分かりますが、unknown を使いこなせると型安全性が格段に上がります。最初は面倒でも、型ガード関数を共通化していけば楽になりますので、ぜひ実践してみてください。
サポートのお願い
下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!

コメント