Блог

Автоматическая генерация типов TypeScript и Python из JSON

Каждый раз, когда вы работаете с JSON API без определений типов, вы действуете вслепую. Имена полей — это строки, форма ответов нигде в коде не задокументирована, а опечатки обнаруживаются только в runtime. Генерация типов напрямую из JSON-примеров устраняет этот разрыв — вы получаете точные, сразу же применимые определения типов без необходимости писать их вручную.

Почему важны определения типов

Типобезопасность

С типизированными ответами API редактор и компилятор обнаруживают несоответствия ещё до запуска кода. Обращение к полю, которого нет в типе, порождает ошибку на этапе компиляции, а не безмолвный undefined в runtime.

Автодополнение и IntelliSense

Определения типов обеспечивают автодополнение в редакторе. Когда вы набираете response.user., редактор показывает все доступные поля с их типами. Это одно лишь это существенно ускоряет разработку при работе с незнакомыми API.

Документация, которая не устаревает

Написанная вручную документация дрейфует. Определения типов, сгенерированные из реальных ответов API, точно отражают фактическую структуру данных, и инструменты могут пересоздавать их при каждом изменении API.

Уверенный рефакторинг

Когда имя поля меняется по всей кодовой базе, типизированный язык позволяет переименовать его везде с уверенностью. Без типов остаётся только grep и надежда на лучшее.

Конвертация JSON в интерфейсы TypeScript

Интерфейсы TypeScript естественно отображаются на объекты JSON. Дан такой ответ API:

{
"id": 42,
"username": "alice",
"email": "alice@example.com",
"role": "admin",
"createdAt": "2024-01-15T09:30:00Z",
"profile": {
"displayName": "Alice Smith",
"avatarUrl": "https://cdn.example.com/avatars/42.png",
"bio": null
},
"tags": ["typescript", "open-source"]
}

Кодогенератор создаёт:

export interface Profile {
displayName: string;
avatarUrl: string;
bio: string | null;
}
export interface User {
id: number;
username: string;
email: string;
role: string;
createdAt: string;
profile: Profile;
tags: string[];
}

Вложенные объекты становятся отдельными именованными интерфейсами, массивы превращаются в типизированные массивы, а значения null порождают union-типы с null.

Улучшение сгенерированных типов

Сгенерированные типы — это отправная точка. После генерации стоит рассмотреть следующие улучшения:

  • Заменить string на union из строковых литералов для известных значений enum: role: "admin" | "editor" | "viewer"
  • Заменить string на брендированный тип для идентификаторов и дат: createdAt: ISODateString
  • Добавить JSDoc-комментарии с описанием семантики полей
  • Явно пометить необязательные поля через ? там, где API может их не возвращать

Конвертация JSON в типы Python

В Python есть два основных подхода для типизированных JSON-данных: dataclass и TypedDict. Каждый подходит для разных сценариев.

TypedDict

TypedDict — наилегковесный вариант. Он описывает форму обычного словаря без необходимости создавать экземпляры. Идеально для данных, которые вы получаете и передаёте дальше:

from typing import TypedDict
class Profile(TypedDict):
displayName: str
avatarUrl: str
bio: str | None
class User(TypedDict):
id: int
username: str
email: str
role: str
createdAt: str
profile: Profile
tags: list[str]

User можно использовать как аннотацию типа везде, где ожидается словарь, и тайп-чекеры вроде mypy и pyright будут валидировать обращения к полям.

Dataclass

dataclass предпочтительнее, когда нужны экземпляры с методами или значениями по умолчанию:

from dataclasses import dataclass
from typing import Optional
@dataclass
class Profile:
displayName: str
avatarUrl: str
bio: Optional[str]
@dataclass
class User:
id: int
username: str
email: str
role: str
createdAt: str
profile: Profile
tags: list[str]

Датаклассы поддерживают __post_init__ для валидации, значения по умолчанию и __eq__ / __repr__ из коробки. Для десериализации сочетайте их с библиотеками вроде dacite или cattrs.

Модель Pydantic

Для API со строгими требованиями к валидации модели Pydantic предлагают наиболее полное решение:

from pydantic import BaseModel
from typing import Optional
class Profile(BaseModel):
displayName: str
avatarUrl: str
bio: Optional[str]
class User(BaseModel):
id: int
username: str
email: str
role: str
createdAt: str
profile: Profile
tags: list[str]
# Десериализация напрямую из словаря
user = User(**response_json)

Pydantic валидирует типы полей при создании экземпляра и выбрасывает подробные ошибки, если данные не соответствуют схеме.

Вывод типов для вложенных объектов и массивов

Хорошие кодогенераторы автоматически обрабатывают сложные структуры.

Вложенные объекты

Каждый вложенный объект становится отдельным именованным типом. Генератор выводит имена из родительского ключа:

{
"order": {
"lineItems": [
{
"productId": "SKU-001",
"quantity": 2,
"unitPrice": 9.99
}
]
}
}

Сгенерированный результат:

export interface LineItem {
productId: string;
quantity: number;
unitPrice: number;
}
export interface Order {
lineItems: LineItem[];
}
export interface Root {
order: Order;
}

Массивы смешанных типов

Когда массив содержит элементы нескольких типов, генераторы создают union:

{ "values": [1, "two", true] }
interface Root {
values: (number | string | boolean)[];
}

Необязательные поля

Когда поле присутствует в одних примерах объектов, но отсутствует в других, хороший генератор помечает его как необязательное:

interface Item {
id: number;
name: string;
description?: string; // отсутствует в некоторых примерах
}

Практический процесс: автогенерация типов ответов API

Надёжный рабочий процесс для типизированной интеграции с API:

ШагДействиеИнструмент
1Захватите реальный пример ответа APIDevTools браузера, Postman, curl
2Сгенерируйте определения типов из примераJSONKit JSON to Code
3Просмотрите и уточните сгенерированные типыВаш редактор
4Добавьте сгенерированные типы в кодовую базуСистема контроля версий
5Перегенерируйте при изменении APIJSONKit JSON to Code

Для API в активной разработке запускайте генератор на актуальном примере при каждом значительном изменении API и сравнивайте результат с вашими текущими типами. Это выявляет breaking changes до того, как они достигнут кода приложения.

Несколько примеров

Если у вас есть несколько JSON-примеров с одного эндпоинта — скажем, ответы с различными заполненными необязательными полями — запустите генератор для каждого и объедините результаты. Объединение всех наблюдаемых полей даёт наиболее полное определение типа.

Использование инструмента JSON to Code в JSONKit

Инструмент JSON to Code на JSONKit конвертирует JSON-примеры в определения типов одним нажатием:

  • Вставьте любой JSON-объект или массив
  • Выберите целевой язык и стиль типов (интерфейс TypeScript, Python TypedDict, dataclass или Pydantic)
  • Получите мгновенно сгенерированные типы с автоматической обработкой вложенных структур
  • Скопируйте результат прямо в проект

Инструмент обрабатывает глубоко вложенные структуры, массивы объектов, nullable-поля и массивы смешанных типов без ручного вмешательства.

Заключение

Писать определения типов для JSON API вручную — утомительно и чревато ошибками. Автогенерация из реальных примеров ответов быстрее, точнее и поддерживает синхронизацию типов с реальностью. Работаете ли вы на TypeScript или Python, сгенерированные типы дают вам защитную сетку типизированного языка без затрат на ручное ведение определений. Сгенерировали один раз, уточнили при необходимости — и перегенерировали, когда API изменился.