Skip to main content

第34章:any卒業③(ユーザー定義型ガード)🧷🧠

ねらい🎯

  • 「anyで通す😇」を卒業して、「unknownから安全に絞り込む✅」ができるようになる
  • APIやJSONなど“外から来るデータ”を、型ガードでチェックしてから使えるようになる📦🔍
  • 型ガード関数にテスト🧪を付けて、「壊してない」安心感を作る

※本日時点の現行最新は TypeScript 5.9.3 だよ〜🆕✨ (GitHub)


まず結論🌸:「型ガード」はこういうもの!

Concept: Traffic Colors

Concept: Gatekeeper

TypeScriptは、実行時(JavaScriptとして動く時)に“型”が消えちゃうんだよね🫧 だから 「これは本当にUserっぽい形?」を実行時にチェックする関数 を自分で作るのが「ユーザー定義型ガード」だよ🧷✨ (TypeScript)


よくある危険パターン(ビフォー)⚠️💥

外から来たデータに対して、いきなり「as」で決めつけちゃうやつ…👇

// ❌ ありがち:決めつけキャスト(実行時チェックが無い)
type UserDto = {
id: string;
name: string;
age?: number;
};

const raw = JSON.parse('{"id": 123, "name": "Mika"}'); // idが数値でも通っちゃう…

const user = raw as UserDto; // ← ここで「UserDtoだよね?」って決めつけ😇

console.log(user.id.toUpperCase()); // 実行時に落ちる可能性あり💥

安全パターン(アフター)✅🛟

ポイントはこれだけ👇

  1. 外部データはまず「unknown」で受ける
  2. 型ガードでチェックしてから使う
type UserDto = {
id: string;
name: string;
age?: number;
};

// まずは「オブジェクトっぽい?」の土台チェック🧱
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}

// ✅ ユーザー定義型ガード(trueのときだけ UserDto として扱える)
function isUserDto(value: unknown): value is UserDto {
if (!isRecord(value)) return false;

// 必須項目チェック
if (typeof value["id"] !== "string") return false;
if (typeof value["name"] !== "string") return false;

// 任意項目チェック(あれば型が正しいこと)
const age = value["age"];
if (age !== undefined && typeof age !== "number") return false;

return true;
}

const raw: unknown = JSON.parse('{"id": "u-001", "name": "Mika", "age": 20}');

if (!isUserDto(raw)) {
throw new Error("UserDtoの形じゃないよ〜🥲");
}

// ここから先は raw が UserDto 扱いになる✨
console.log(raw.id.toUpperCase());
console.log(raw.age?.toFixed(0));

型ガード関数は「戻り値の型」に「parameterName is Type」みたいな“型述語”を書くのがコツだよ🧷✨ (TypeScript)


手順(小さく刻む)👣✨

1) any を unknown に変える🧷

  • 外部データ(JSON.parse / fetch / localStorage / 3rd party SDK)はまず unknown で受ける🫧

2) “土台ガード”を作る🧱

  • 「オブジェクト?」「nullじゃない?」「配列じゃない?」を最初にやると事故が減るよ🚧

3) “ドメイン用ガード”を作る🧩

  • 必須プロパティ:型までチェック✅
  • 任意プロパティ:存在するなら型チェック✅

4) 使う場所は “境界(I/Oの近く)” に寄せる🚪

  • 入力直後にチェックして、通ったら中では安心して扱う🛟

もっと便利ワザ①:filter で配列をキレイにする🧹🌀

型ガードは「配列のfilter」と相性よすぎる〜!💖

const list: unknown[] = [
{ id: "u-1", name: "Mika" },
{ id: 999, name: "NG" },
null,
{ id: "u-2", name: "Rina", age: 19 },
];

const users = list.filter(isUserDto);
// users は UserDto[] になる✨(変なやつは落ちる🧹)

console.log(users.map(u => u.name));

もっと便利ワザ②:「asserts」で“通らなければ例外”にする🔥🧯

「ifで分岐するより、通らなかったら即エラーにしたい」ならアサーション関数が便利だよ😊 (例外を投げる代わりに、通った後は型が確定するやつ!) (TypeScript)

function assertUserDto(value: unknown): asserts value is UserDto {
if (!isUserDto(value)) {
throw new Error("UserDtoじゃないよ〜🥲");
}
}

const raw: unknown = JSON.parse('{"id":"u-3","name":"Nana"}');
assertUserDto(raw);

// ここから raw は UserDto として確定✨
console.log(raw.id);

“DTOを安全に検証”ミニ実戦📦✅(ありがちシーン)

「APIから来たJSONをDTOとして扱いたい」ってときの王道ムーブだよ〜🧑‍💻✨

async function fetchUserJson(): Promise<unknown> {
// 例:fetchして json() した結果(実際はAPIが返す)
return JSON.parse('{"id":"u-10","name":"Mika","age":20}');
}

async function main() {
const raw = await fetchUserJson(); // unknown

if (!isUserDto(raw)) {
// ログに入れておくと調査しやすい🪵🔍
console.error("invalid dto", raw);
return;
}

// ここからは安全✨
console.log(raw.name);
}

main();

テスト(型ガードは絶対テストして守る🧪🛡️)

型ガードは「通す・落とす」が命! だからユニットテストで “OK/NG” の例を固定しちゃうのが超おすすめ💕

(例:Vitestっぽい書き方)

import { describe, it, expect } from "vitest";

describe("isUserDto", () => {
it("OK: 正しい形なら true", () => {
const v: unknown = { id: "u-1", name: "Mika", age: 20 };
expect(isUserDto(v)).toBe(true);
});

it("NG: idがstringじゃない", () => {
const v: unknown = { id: 123, name: "Mika" };
expect(isUserDto(v)).toBe(false);
});

it("NG: null", () => {
const v: unknown = null;
expect(isUserDto(v)).toBe(false);
});

it("NG: 余計な型のage", () => {
const v: unknown = { id: "u-1", name: "Mika", age: "20" };
expect(isUserDto(v)).toBe(false);
});
});

よくある落とし穴まとめ⚠️🕳️

  • 「typeof value === "object"」だけだと null が混ざる(nullもobject判定)🙅‍♀️
  • 配列もobject扱いだから、必要なら Array.isArray で弾く🙅‍♀️
  • 「as」で逃げると、チェックしてないのに“型だけ正しい顔”をする😇
  • 任意プロパティは「あるなら型チェック」までやると事故が減る✅

ミニ課題✍️🌷

お題:ProductDto を安全にしたい🛒✨

次の型に対して「isProductDto(value: unknown): value is ProductDto」を作ってみてね!

  • id は string
  • title は string
  • price は number
  • tags は string[](任意:無いならOK)
type ProductDto = {
id: string;
title: string;
price: number;
tags?: string[];
};

できたらテストも4本くらい作ろう🧪💪(OK1本、NG3本くらいがおすすめ)


AI活用ポイント🤖✅(お願い方+チェック観点)

お願い方(コピペ用)📝🤖

  • 「unknown入力 value を ProductDto に絞り込む型ガード関数 isProductDto を書いて。null/配列/任意プロパティ(tags)も考慮して」
  • 「isProductDto のユニットテストを Vitest で。OK1本、NG3本。落とす理由が分かるケースにして」

チェック観点(AIの提案を採用する前に👀✅)

  • null を弾けてる?🫧
  • 配列を弾けてる?🧺
  • 必須プロパティの型チェックがある?🧷
  • tags の「配列の中身が全部string」まで見てる?🔍
  • 「as」で雑にキャストしてごまかしてない?⚠️

まとめ🧁✨

  • 外部データは unknown で受ける🧷
  • 型ガードで「本当にその形?」をチェックしてから使う✅
  • filter や asserts を使うと、実務が一気にラクになるよ〜🛟💕 (TypeScript)