← ブログに戻る

JSONからTypeScript型を生成する方法

JSONスキーマからTypeScript型定義を自動生成。ツール、技術、ベストプラクティス。

Big JSON Team14 分で読めますprogramming
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

14 分読む

# JSONからTypeScript型を生成する方法

JSONデータからTypeScript型定義を自動生成する方法を学びます。

なぜ型生成が必要か

利点

  • 型安全性 - コンパイル時のエラー検出
  • 自動補完 - IDEサポート
  • ドキュメント - 型が仕様書になる
  • リファクタリング - 安全な変更
  • 時間節約 - 手動で書く必要なし

Before(型なし)

const user = JSON.parse(response);

console.log(user.name); // タイポの可能性

After(型あり)

interface User {

id: number;

name: string;

email: string;

}

const user: User = JSON.parse(response);

console.log(user.name); // 型安全

オンラインツール

1. QuickType

URL: quicktype.io 特徴:
  • 最も強力
  • 複数言語サポート
  • JSON、JSONスキーマから変換
  • カスタマイズ可能

使い方:
  • JSONを貼り付け
  • 出力言語でTypeScriptを選択
  • 生成されたコードをコピー
  • 例:

    入力JSON:

    {
    

    "id": 1,

    "name": "田中太郎",

    "email": "tanaka@example.com",

    "isActive": true

    }

    出力TypeScript:

    export interface User {
    

    id: number;

    name: string;

    email: string;

    isActive: boolean;

    }

    2. JSON2TS

    URL: json2ts.com 特徴:
    • シンプル
    • ルートタイプ名のカスタマイズ
    • プレフィックス/サフィックス

    オプション:
    • プレフィックス追加(例: I)
    • オプショナルプロパティ
    • 読み取り専用

    3. Transform Tools

    URL: transform.tools/json-to-typescript 特徴:
    • クリーンなUI
    • リアルタイム変換
    • 複数フォーマット

    4. BigJSON.online

    JSONビューアーとしても型のヒントを提供。

    CLIツール

    1. quicktype (CLI)

    インストール

    npm install -g quicktype

    基本的な使い方

    # JSONファイルから型生成
    

    quicktype data.json -o types.ts

    # 複数ファイル

    quicktype user.json product.json -o types.ts

    # 標準入力から

    echo '{"name":"太郎","age":30}' | quicktype -o User.ts

    高度なオプション

    # トップレベル名を指定
    

    quicktype data.json --top-level User -o User.ts

    # JSONスキーマから

    quicktype schema.json --src-lang schema -o types.ts

    # インターフェースではなくクラス

    quicktype data.json --just-types -o types.ts

    # readonly プロパティ

    quicktype data.json --readonly -o types.ts

    2. json-schema-to-typescript

    インストール

    npm install -g json-schema-to-typescript

    使い方

    json2ts -i schema.json -o types.d.ts

    3. NPMスクリプト統合

    {
    

    "scripts": {

    "generate-types": "quicktype src/data/.json -o src/types.ts",

    "types:watch": "nodemon --watch src/data --exec npm run generate-types"

    }

    }

    プログラムでの生成

    Node.js スクリプト

    const { quicktype, InputData, JSONSchemaInput } = require('quicktype-core');
    
    

    async function generateTypes(jsonString, typeName) {

    const jsonInput = new JSONSchemaInput(new InputData());

    await jsonInput.addSource({

    name: typeName,

    schema: jsonString,

    });

    const inputData = new InputData();

    inputData.addInput(jsonInput);

    const result = await quicktype({

    inputData,

    lang: 'typescript',

    rendererOptions: {

    'just-types': 'true',

    },

    });

    return result.lines.join('\n');

    }

    // 使用例

    const json = JSON.stringify({

    id: 1,

    name: '田中太郎',

    email: 'tanaka@example.com',

    });

    generateTypes(json, 'User').then(types => {

    console.log(types);

    });

    TypeScriptコンパイラAPI

    import  as ts from 'typescript';
    
    

    function jsonToInterface(json: any, interfaceName: string): string {

    const properties: string[] = [];

    for (const [key, value] of Object.entries(json)) {

    const type = getTypeScriptType(value);

    properties.push( ${key}: ${type};);

    }

    return interface ${interfaceName} {\n${properties.join('\n')}\n};

    }

    function getTypeScriptType(value: any): string {

    if (value === null) return 'null';

    if (Array.isArray(value)) {

    if (value.length === 0) return 'any[]';

    return ${getTypeScriptType(value[0])}[];

    }

    if (typeof value === 'object') {

    return 'any'; // または再帰的に処理

    }

    return typeof value;

    }

    // 使用

    const json = { id: 1, name: '田中', active: true };

    console.log(jsonToInterface(json, 'User'));

    JSONスキーマからの生成

    スキーマ定義

    {
    

    "$schema": "http://json-schema.org/draft-07/schema#",

    "title": "User",

    "type": "object",

    "properties": {

    "id": {

    "type": "integer",

    "description": "ユーザーID"

    },

    "name": {

    "type": "string",

    "minLength": 1

    },

    "email": {

    "type": "string",

    "format": "email"

    },

    "age": {

    "type": "integer",

    "minimum": 0

    },

    "tags": {

    "type": "array",

    "items": {

    "type": "string"

    }

    }

    },

    "required": ["id", "name", "email"]

    }

    生成されるTypeScript

    /*
    

    User

    /

    export interface User {

    /

    ユーザーID

    /

    id: number;

    name: string;

    email: string;

    age?: number;

    tags?: string[];

    }

    json-schema-to-typescript使用

    import { compile } from 'json-schema-to-typescript';
    

    import as fs from 'fs';

    const schema = JSON.parse(fs.readFileSync('user-schema.json', 'utf8'));

    compile(schema, 'User', {

    bannerComment: '',

    style: {

    semi: true,

    singleQuote: true,

    },

    }).then(ts => {

    fs.writeFileSync('User.d.ts', ts);

    });

    複雑な型の処理

    ネストされたオブジェクト

    入力:

    {
    

    "user": {

    "id": 1,

    "name": "田中太郎",

    "address": {

    "city": "東京",

    "zip": "100-0001"

    }

    }

    }

    出力:

    export interface Address {
    

    city: string;

    zip: string;

    }

    export interface User {

    id: number;

    name: string;

    address: Address;

    }

    export interface RootObject {

    user: User;

    }

    配列とジェネリクス

    入力:

    {
    

    "users": [

    {

    "id": 1,

    "name": "田中太郎"

    }

    ],

    "posts": [

    {

    "id": 1,

    "title": "Hello"

    }

    ]

    }

    出力:

    export interface User {
    

    id: number;

    name: string;

    }

    export interface Post {

    id: number;

    title: string;

    }

    export interface RootObject {

    users: User[];

    posts: Post[];

    }

    ユニオン型

    入力(異なる型の値):

    {
    

    "items": [

    {"type": "text", "content": "Hello"},

    {"type": "image", "url": "pic.jpg"}

    ]

    }

    出力:

    export interface TextItem {
    

    type: 'text';

    content: string;

    }

    export interface ImageItem {

    type: 'image';

    url: string;

    }

    export type Item = TextItem | ImageItem;

    export interface RootObject {

    items: Item[];

    }

    VS Code統合

    設定

    {
    

    "json.schemas": [

    {

    "fileMatch": ["data/.json"],

    "url": "./schemas/data-schema.json"

    }

    ]

    }

    タスク自動化

    {
    

    "version": "2.0.0",

    "tasks": [

    {

    "label": "Generate Types",

    "type": "shell",

    "command": "quicktype",

    "args": [

    "src/data.json",

    "-o",

    "src/types.ts"

    ],

    "group": {

    "kind": "build",

    "isDefault": true

    }

    }

    ]

    }

    APIレスポンスからの型生成

    自動化スクリプト

    import fetch from 'node-fetch';
    

    import { quicktype } from 'quicktype-core';

    import as fs from 'fs';

    async function generateTypesFromAPI(url: string, typeName: string) {

    // APIからデータ取得

    const response = await fetch(url);

    const data = await response.json();

    // サンプルデータを保存

    fs.writeFileSync('sample.json', JSON.stringify(data, null, 2));

    // 型を生成

    const types = await generateTypes(JSON.stringify(data), typeName);

    // 型定義を保存

    fs.writeFileSync(${typeName}.ts, types);

    }

    // 使用

    generateTypesFromAPI(

    'https://api.example.com/users',

    'User'

    );

    Postman統合

  • Postmanでリクエスト実行
  • レスポンスをコピー
  • QuickTypeに貼り付け
  • 生成された型を保存
  • ベストプラクティス

    1. 適切な型名

    // 良い
    

    export interface User {

    id: number;

    name: string;

    }

    // 悪い

    export interface Object1 {

    id: number;

    name: string;

    }

    2. オプショナルプロパティ

    export interface User {
    

    id: number;

    name: string;

    email: string;

    phone?: string; // オプション

    address?: Address;

    }

    3. 読み取り専用プロパティ

    export interface User {
    

    readonly id: number;

    name: string;

    email: string;

    }

    4. 厳格なnullチェック

    export interface User {
    

    id: number;

    name: string;

    bio: string | null; // 明示的にnullを許可

    }

    5. ドキュメントコメント

    /*
    

    ユーザー情報

    /

    export interface User {

    /

    一意のユーザーID

    /

    id: number;

    /

    ユーザーの表示名

    /

    name: string;

    }

    自動化ワークフロー

    Git Hooks

    #!/bin/bash
    

    # .git/hooks/pre-commit

    # JSONファイルが変更されたら型を再生成

    for file in $(git diff --cached --name-only | grep '.json$'); do

    if [ -f "$file" ]; then

    quicktype "$file" -o "${file%.json}.ts"

    git add "${file%.json}.ts"

    fi

    done

    CI/CD統合

    # .github/workflows/generate-types.yml
    

    name: Generate Types

    on:

    push:

    paths:

    - '.json'

    jobs:

    generate:

    runs-on: ubuntu-latest

    steps:

    - uses: actions/checkout@v2

    - name: Setup Node

    uses: actions/setup-node@v2

    - name: Install quicktype

    run: npm install -g quicktype

    - name: Generate types

    run: |

    quicktype src/data/.json -o src/types.ts

    - name: Commit changes

    run: |

    git config user.name "GitHub Actions"

    git config user.email "actions@github.com"

    git add src/types.ts

    git commit -m "Update generated types" || exit 0

    git push

    Watch モード

    // watch-types.js
    

    const chokidar = require('chokidar');

    const { exec } = require('child_process');

    const watcher = chokidar.watch('src/*/.json');

    watcher.on('change', (path) => {

    console.log(${path} が変更されました。型を再生成中...);

    exec(quicktype ${path} -o ${path.replace('.json', '.ts')},

    (error, stdout, stderr) => {

    if (error) {

    console.error(エラー: ${error});

    return;

    }

    console.log('型の生成完了!');

    }

    );

    });

    console.log('JSONファイルを監視中...');

    トラブルシューティング

    問題1: 型が"any"になる

    原因: JSONデータが不十分 解決:
    • より完全なサンプルデータを使用
    • JSONスキーマを使用
    • 手動で型を修正

    問題2: ネストが深すぎる

    解決:
    // 型を分割
    

    export interface Address {

    city: string;

    }

    export interface User {

    address: Address;

    }

    問題3: 重複する型定義

    解決:
    • 共通の型を抽出
    • 型のエイリアスを使用

    ツール比較

    | ツール | CLI | API | スキーマ対応 | カスタマイズ |

    |--------|-----|-----|-------------|-------------|

    | QuickType | ✅ | ✅ | ✅ | ⭐⭐⭐⭐⭐ |

    | json-schema-to-typescript | ✅ | ✅ | ✅ | ⭐⭐⭐⭐ |

    | JSON2TS | ❌ | ❌ | ❌ | ⭐⭐ |

    | Transform Tools | ❌ | ❌ | ❌ | ⭐⭐⭐ |

    まとめ

    クイックスタート

    # オンライン
    

    # quicktype.io にアクセス

    # CLI

    npm install -g quicktype

    quicktype data.json -o types.ts

    # Node.js

    npm install quicktype-core

    推奨ワークフロー

  • APIレスポンスをサンプルとして保存
  • QuickTypeで型生成
  • 必要に応じて手動で調整
  • 型定義をバージョン管理
  • CI/CDで自動更新
  • 型生成で、TypeScriptプロジェクトの型安全性を向上させましょう!

    Share:

    関連記事

    Read in English