JSON в TypeScript Автоматическое создание интерфейсов из ответов API
Ручное написание интерфейсов TypeScript для каждого ответа API является трудоемким и подверженным ошибкам. Узнайте, как автоматически генерировать точные интерфейсы на основе реальных данных JSON, а затем добавьте проверку на выполнение в режиме выполнения с помощью Zod — поскольку типы исчезают на уровне выполнения и `any` не является решением.
Вы только что получили данные из внешнего API. Ответ — это плотный блок JSON — вложенные объекты, массивы, поля с возможным отсутствием — и теперь вам нужно определить типы в TypeScript. Поэтому вы открываете новый файл и начинаете вводить текст interface User { ... }, и через 20 минут у вас получается что-то, что, вероятно, соответствует реальным данным. Вероятно.
Существует лучший способ. Инструменты, которые конвертируют JSON в интерфейсы TypeScript, сокращают эту 20-минутную задачу до нескольких секунд. В этой статье описаны типы, которые генерируются, как обрабатывать сложные случаи (отсутствующие значения, объединения, глубокая вложенность), и почему вы должны использовать генерированные типы вместе с схемами Zod для обнаружения несоответствий формы на уровне выполнения — а не только на уровне компиляции.
Почему интерфейсы TypeScript для ответов API важны
Преимущество TypeScript — обнаружение ошибок до выполнения кода. Без типизированных ответов API вы летите в темноту: обращаетесь к свойствам, которые могут не существовать, рассматриваете необязательные значения как обязательные или тихо преобразуете строку "null" в что-то неожиданное в дальнейшем.
Рассмотрим распространённую ситуацию:
const user = await fetchUser(id);
console.log(user.address.city); // TypeError at runtime if address is null
Если бы вы правильно типизировали ответ — с address: Address | null — TypeScript сразу бы выявила эту ошибку. Компилятор является вашей первой линией защиты, но только если вы предоставите ему что-то на что он сможет работать.
Ручное написание интерфейсов для каждого API является трудоёмким и подверженным ошибкам. Вы неверно читаете схему, пропускаете необязательное поле или копируете устаревшую версию. Генерация интерфейсов напрямую из реальных данных JSON устраняет эту человеческую ошибку.
Что даёт конвертация JSON в TypeScript
Рассмотрим простой ответ API:
{
"id": 42,
"username": "jsmith",
"email": "j@example.com",
"createdAt": "2024-01-15T10:30:00Z",
"role": "admin",
"profile": {
"bio": "Developer",
"avatar": null
}
}
Вставьте это в Генератор интерфейсов TypeScript из JSON и вы получите:
interface Profile {
bio: string;
avatar: null;
}
interface RootObject {
id: number;
username: string;
email: string;
createdAt: string;
role: string;
profile: Profile;
}
Несколько моментов, на которые стоит обратить внимание:
- Вложенные объекты превращаются в собственные интерфейсы —
Profileизвлекается автоматически, а не встраивается. - Даты типизируются как
string— JSON не имеет типа даты, поэтому строки в формате ISO остаются строками. Вам нужно будет их самостоятельно парсить. avatar: nullтипизируется как литералnull— что является точным, но неполным. Более подробно об этом ниже.
Обработка сложных случаев
Поля с возможным отсутствием
при наличии в вашем образце JSON, генератор типизирует их как null . Но на практике это поле, вероятно, меняется между реальным значением и null в зависимости от данных. Вам нужно вручную скорректировать эти типы: nullТо же самое касается необязательных полей, которые в вашем образце оказываются заполненными — добавьте
// Generated
avatar: null;
// What you actually want
avatar: string | null;
к любому свойству, которое может отсутствовать в некоторых ответах. ? Массивы объектов обрабатываются чисто. При наличии:
Массивы
Генератор создаёт:
{
"posts": [
{ "id": 1, "title": "Hello", "published": true },
{ "id": 2, "title": "World", "published": false }
]
}
Если в ваших образцах JSON показано, что поле содержит разные типы в разных записях — например, поле
interface Post {
id: number;
title: string;
published: boolean;
}
interface RootObject {
posts: Post[];
}
Объединяющие типы
которое может быть числом или строкой — вы должны представить это как объединение. Генерированные типы не обнаружат это на основе одного образца, поэтому стоит проверить документацию API: value Глубокая вложенность объектов
value: string | number;
Глубокая вложенность — это то, где ручное типирование действительно разрушается — и где генераторы получают свою ценность. Ответ с тремя или четырьмя уровнями вложенности разбивается на чистую иерархию именованных интерфейсов, каждый из которых отвечает за свою форму.
Разрыв между типами на уровне компиляции и реальностью на уровне выполнения
Вот то, что часто пропускают новички в TypeScript:
типы исчезают на уровне выполнения. TypeScript компилируется в JavaScript, и JavaScript не имеет понятия об интерфейсах. Если ваш API возвращает форму, не соответствующую объявленному типу, TypeScript не узнает — и не предупредит вас. Это делает распространённый паттерн приведения ответов API действительно опасным:
Это приведение говорит TypeScript «верю, что это
const data = await response.json() as User; // No validation, just trust
» — но TypeScript не может проверить это. Если API изменяет свою форму или возвращает объект ошибки вместо ответа, ваш код ломается на уровне выполнения, и компилятор никогда не предупредил вас. UserРешением является проверка на уровне выполнения.
Zod для проверки на уровне выполнения
Zod
— это библиотека валидации схем, ориентированная на TypeScript. Вы определяете схему один раз, используете её для парсинга входных данных и получаете полностью типизированное значение — или подробную ошибку, если форма не соответствует. Без приведения, без предположений. Используя тот же образец JSON, который был ранее, Zod создаёт:
Обратите внимание на последнюю строку: Генератор JSON в Zod Schema получает тип TypeScript напрямую из схемы. Вы получаете как безопасность на уровне компиляции, так и проверку на уровне выполнения из одного источника правды.
import { z } from "zod";
const ProfileSchema = z.object({
bio: z.string(),
avatar: z.null(),
});
const RootObjectSchema = z.object({
id: z.number(),
username: z.string(),
email: z.string(),
createdAt: z.string(),
role: z.string(),
profile: ProfileSchema,
});
type RootObject = z.infer<typeof RootObjectSchema>;
Использование в точке получения данных выглядит так: z.infer Исправьте сгенерированную схему, чтобы обработать случаи с отсутствующими полями, которые генератор не может определить на основе одного образца:
Интерфейс против типа: какой вы должны выбрать?
const rawData = await response.json();
const user = RootObjectSchema.parse(rawData); // throws if shape is wrong
// Or use safeParse to avoid throwing:
const result = RootObjectSchema.safeParse(rawData);
if (!result.success) {
console.error(result.error.issues);
} else {
console.log(result.data.username); // fully typed
}
Алиасы могут представлять формы объектов в TypeScript, и для большинства типизации ответов API они взаимозаменяемы. Практические различия:
avatar: z.string().nullable(), // was z.null()
bio: z.string().optional(), // if the field might be absent
Интерфейсы могут расширяться и объединяться
Оба interface и type — полезно, если вы хотите расширять базовый тип по файлам. Объединение объявлений позволяет добавлять поля к интерфейсу, определённому в другом месте.
- Алиасы типов более гибки — они могут представлять объединения, пересечения, кортежи и отображённые типы, что интерфейсы не могут сделать.
- Сообщения об ошибках обычно более чёткие с интерфейсами — TypeScript расширяет алиасы в сообщениях об ошибках, что может сделать глубоко вложенные ошибки трудными для чтения.
- Для форм ответов API, любой из них подойдёт. Выберите если вы ожидаете расширение типа; используйте
если вам нужно использовать семантику объединения или пересечения. Важнее всего поддерживать единообразие в рамках кодовой базы, чем выбирать один из двух. interface Практический рабочий процесс type Вот рабочий процесс, который занимает минуты, а не целый день:
Соберите реальный ответ.
Используйте вкладку сети браузера, Postman или curl, чтобы получить реальный ответ API. Чем полнее образец, тем лучше будут сгенерированные типы.
- Сгенерируйте интерфейс TypeScript. Вставьте JSON в
- . Скопируйте результат в ваш проект. Сгенерируйте схему Zod. Генератор интерфейсов TypeScript из JSONВставьте тот же JSON в
- . Скопируйте это в ваш проект. Проверьте на наличие полей с отсутствием и необязательными значениями. Генератор JSON в Zod SchemaПроверьте сгенерированный вывод на поля, типизированные как литерал
- или поля, которые могут отсутствовать. Обновите их до Проверка на уровне получения данных.
nullЗамените любыеstring | null,.nullable(), или.optional()по мере необходимости. - на . Теперь тип гарантированно соответствует, а не просто предполагается.
as YourTypeЭто полный цикл — от сырого JSON до безопасности на уровне компиляции и гарантий на уровне выполнения за несколько минут.YourSchema.parse()илиsafeParse()JSON в TypeScript: Автоматическая генерация интерфейсов из ответов API 2
JSON в TypeScript: Автоматическая генерация интерфейсов из ответов API 1
Установите наши расширения
Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска
恵 Табло результатов прибыло!
Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!
Подписаться на новости
все Новые поступления
всеОбновлять: Наш последний инструмент was added on Июн 24, 2026
