2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおける型安全へのアプローチ:JSDoc・TypeScript移行・ランタイムバリデーション設計戦略

Posted at

概要

JavaScriptは「何でも通る」がゆえに、“何が通っているのか分からない”という問題に陥る。
型の保証は、バグの早期検出だけでなく、関数の意味、責務の境界、APIの信頼性を明示する設計行為である。

本稿では、JavaScriptに型安全をもたらす実践的戦略を以下の観点で整理する:

  • JSDocによる静的型注釈
  • TypeScriptへの段階的移行
  • ランタイム型バリデーションとの併用
  • JSONスキーマ/Zodなどの活用
  • 型とテストの相補関係

1. JSDocによる型注釈

/**
 * @param {string} name
 * @param {number} age
 * @returns {{ name: string, age: number }}
 */
function createUser(name, age) {
  return { name, age };
}
  • ✅ 型チェッカー付きエディタで補完が効く
  • ✅ VSCode + checkJs = 軽量な型検出が可能
  • ✅ ライブラリ/共通処理に限定しても効果大

2. TypeScriptへの漸進的移行

// user.js → user.ts にリネーム
export type User = {
  id: string;
  email: string;
};

export function isAdult(user: User): boolean {
  return user.age >= 18;
}
  • ✅ 1ファイルずつ .ts 化しつつ、型定義を明示的に導入
  • // @ts-nocheck による一時的な例外許容も可能

「型の精度 < 型の存在」からスタートする設計マインドが重要


3. ランタイムバリデーションの必要性

静的型は“書いてあるコードに対してのみ有効”。
外部入力やユーザー入力などには無力。

function parseUser(data) {
  if (typeof data.name !== 'string') throw new Error('Invalid name');
  if (typeof data.age !== 'number') throw new Error('Invalid age');
  return data;
}

→ ✅ 静的型(TypeScript)+ ランタイムチェックの併用が必須


4. バリデーションライブラリによる型保証

✅ Zod の例

import { z } from 'zod';

const UserSchema = z.object({
  name: z.string(),
  age: z.number().int().gte(0)
});

type User = z.infer<typeof UserSchema>;

const user = UserSchema.parse(input); // ✅ 型安全 + バリデーション一体
  • ✅ ランタイムチェック + 静的型定義がセットで構築可能
  • ✅ スキーマ変更がそのまま型変更にも直結

5. JSON Schemaによる構造定義と互換性チェック

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name", "age"]
}
  • ✅ バックエンドやAPIドキュメントと一致しやすい
  • ✅ ajv等での検証が容易

6. 型定義とテストの補完関係

  • ✅ 型は「書かれていることの正しさ」を保証
  • ✅ テストは「動作していることの正しさ」を保証
test('createUser returns correct shape', () => {
  const user = createUser('Toto', 28);
  expect(user).toMatchObject({ name: 'Toto', age: 28 });
});

→ ✅ 型とテストは**“補い合う関係”**であり、どちらも必要


設計判断フロー

① この関数、入力型が曖昧? → JSDocで明記

② 型の恩恵が足りない? → `.ts` で移行開始

③ 外部入力を受け取る? → ランタイムバリデーション追加

④ 型とバリデーションの二重管理がつらい? → ZodやSchemaベースで一元化

⑤ API仕様と一致させたい? → JSON Schemaで連携設計

よくあるミスと対策

❌ 型が定義されているのに入力が保証されていない

→ ✅ ランタイムバリデーションと併用


❌ 型定義が増えてもテストが存在しない

→ ✅ テストは型の“意味”を裏付ける存在


❌ 型を“使わないこと”が許されているコード

→ ✅ // @ts-check などで段階的に型推進を促す


結語

型とは「制約」ではない。
それは**“コードに意味を与え、エラーを前提としない設計”**を可能にする最小単位のガイドラインである。

  • 静的型で安心を得て
  • ランタイムで現実と整合し
  • 設計の意図をコードで語る

JavaScriptは自由な言語であるがゆえに、
型による“設計的拘束”は、最も強い自由を生む。

2
3
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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?