JSON в TypeScript
Вставьте JSON и получите чистые TypeScript-интерфейсы: вложенные объекты, типизированные массивы, дедупликация форм и своё имя корня. Работает в браузере.
Зачем генерировать типы из JSON
Когда API возвращает ответ, кто-то должен описать его в TypeScript руками — поле за полем, уровень за уровнем. Это долго, а вторая версия эндпоинта делает описание устаревшим. Инструмент читает JSON один раз и пишет интерфейсы за вас: каждый вложенный объект становится интерфейсом, массивы — типизированными списками, а одинаковые формы автоматически схлопываются в один интерфейс.
Что умеет
- Интерфейсы для каждого вложенного объекта с корректными именами полей — с кавычками там, где нужно.
- Типизированные массивы, включая объединения, когда массив смешивает разные формы.
- Дедупликация форм и своё имя корневого интерфейса.
Как пользоваться
- Вставьте JSON — ответ API, конфиг, любой валидный.
- При желании поменяйте имя корневого интерфейса.
- Скопируйте готовый TypeScript в свой проект.
Как это работает
Генератор рекурсивно обходит разобранный JSON, выводит тип для каждого значения, называет интерфейсы объектов в стиле PascalCase по имени ключа и запоминает сигнатуру каждой формы, чтобы одинаковые объекты переиспользовали один интерфейс. Всё выполняется локально в браузере.
Зачем типы описывать по ответам API
Обычная история разработки: фронтенд получил от бэкенда ответ, открыл его в консоли, скопировал в буфер и начал переписывать в интерфейс. Через час работы выясняется, что поле называется не так, а вложенный объект оказался массивом объектов. Ещё через час выясняется, что в тестовом окружении формат ответа немного другой, и описание снова надо править. Генератор типов делает эту работу за секунду: вставили JSON, получили готовые интерфейсы и вставили их в проект.
Второй сценарий, не менее частый, это работа с конфигурациями и с сохранёнными ответами. Файл конфигурации, выгрузка из базы, сохранённый ответ стороннего сервиса, тестовый фикстур. Всё это JSON, и всё это хочется использовать в TypeScript-проекте с типизацией, чтобы редактор подсказывал поля. Ещё удобнее, когда описание лежит рядом с данными и меняется вместе с ними.
Третий сценарий, о котором стоит сказать отдельно, это проверка гипотезы. Нужно убедиться, что форма ответа понятна, что вложенные структуры однотипны и что одинаковые по форме объекты можно описать одним интерфейсом, а не десятью копиями. Здесь генератор выступает как инструмент анализа: посмотрите на получившееся дерево интерфейсов, и структура данных станет видна. Для сравнения двух версий ответа пригодится сравнение JSON, а для проверки схемы валидатор JSON Schema.
- Описание ответа API, который только что изменился в соседнем сервисе.
- Типизация конфигурационного файла, который лежит в репозитории.
- Создание интерфейса для тестовой фикстуры или мока ответа.
- Разбор структуры вложенного ответа, который трудно прочитать в консоли.
- Проверка, одинаковы ли по форме объекты в разных ветках ответа.
- Быстрое получение черновика типа для эксперимента в редакторе.
- Обновление описания после смены формата ответа у партнёра.
- Подготовка примера в документации, где нужен читаемый тип данных.
Ошибки, из-за которых типы получаются не такими
Первая ошибка, которая бросается в глаза, это неправильные имена интерфейсов. Инструмент выводит имя по ключу и по привычному правилу убирает множественное число, но правило работает только для английской формы. Ключ `address` превращается в `Addres`, `status` в `Statu`, `series` в `Serie`, потому что буква `s` на конце считается признаком множественного числа. С `categories` при этом получается правильное `Category`, а вот с русскими или неанглийскими ключами будет совсем не то. Имена стоит поправить руками.
Вторая ошибка касается того, что типы получаются обязательными. Каждое поле помечается как присутствующее, хотя в реальном ответе часть полей может отсутствовать. Если поле иногда приходит, а иногда нет, в описании нужно добавить знак вопроса, иначе код будет считать, что значение всегда определено, и упадёт на `undefined` при разборе. Третья ошибка связана с типами-объединениями: если в массиве смешаны разные формы, генератор честно выдаст объединение, но такое объединение в реальном коде приходится сужать вручную, и это нормальная часть работы.
Четвёртая ошибка касается полей со значением `null`. Инструмент превращает его в тип `null`, и это верно, но в реальном ответе поле может быть как `null`, так и обычным значением, и тогда нужен тип вроде `string | null`. Та же история с пустыми массивами: из них выводится `unknown[]`, потому что выводить больше не из чего, и это честнее, чем угадывать. Пятая ошибка, о которой стоит помнить, в том, что инструмент не знает про даты. Строка `2024-03-10` останется строкой `string`, и если в проекте используется `Date`, тип придётся поправить. Перед правкой полезно посмотреть, что вообще пришло, через форматтер JSON.
- Имена интерфейсов в единственном числе образованы неверно: `address` даёт `Addres`, `status` даёт `Statu`.
- Все поля помечены как обязательные, хотя часть из них иногда отсутствует.
- Поле с `null` описано только как `null`, без варианта обычного значения.
- Пустой массив превращён в `unknown[]` и остался без уточнения.
- Даты в ответе остались строковым типом, хотя в проекте используется `Date`.
- Ожидание, что генератор создаст `type` вместо `interface` или добавит `readonly`.
- Попытка получить перечисление из поля со строковыми значениями: получится обычный `string`.
- Ожидание, что вложенные объекты с одинаковыми полями получат разные описания. Наоборот, они схлопываются.
Как устроен разбор JSON и что он умеет
Работа начинается с `JSON.parse`, и если текст невалиден, парсер вернёт сообщение об ошибке с указанием позиции, а генерация не запустится. Дальше по разобранной структуре идёт рекурсивный обход. Для каждого значения определяется тип: `null` становится `null`, строки, числа и логические значения получают соответствующие примитивы, а массив превращается в список. Если элементы массива однотипные, получается `T[]`, а если разные, то объединение в скобках, например `(string | number)[]`.
Когда обход доходит до объекта, для него создаётся отдельный интерфейс. Имя берётся из ключа, по которому объект встретился, и переводится в PascalCase: `user_profile` становится `UserProfile`. Ключи, которые не являются корректными идентификаторами TypeScript, берутся в кавычки, как и требует язык. Чтобы одинаковые по форме объекты не плодили копии, для каждого набора полей считается подпись, поля сортируются и складываются в строку, и одинаковые подписи приводят к одному общему интерфейсу.
Отдельная деталь, которую полезно знать, касается корневого элемента. Если в JSON лежит объект, для него создаётся интерфейс с указанным вами именем, по умолчанию `Root`. Если в корне массив, описание принимает вид `export interface Root { value: T[]; }`, то есть массив оборачивается в поле. Если в корне примитив, например число, получится интерфейс с единственным полем `value`. Вложенные интерфейсы выводятся раньше корневого, и все они помечаются словом `export`, чтобы их можно было импортировать по отдельности.
Как разные значения JSON превращаются в типы
| Значение в JSON | Тип в TypeScript | Комментарий |
|---|---|---|
| "текст" | string | Тип определяется по значению |
| 42 | number | Целые и дробные дают один тип |
| true | boolean | Логические значения различаются |
| null | null | Объединение с другими типами не создаётся |
| [1, 2, 3] | number[] | Однотипные элементы дают простой список |
| ["a", 1] | Объединение string и number | Смешанный массив даёт объединение типов в скобках |
| [] | unknown[] | Выводить тип не из чего |
| {...} | Отдельный интерфейс | Имя берётся из ключа |
| "key-with-dash" | "key-with-dash": string | Некорректный идентификатор берётся в кавычки |
Одинаковые по набору полей объекты переиспользуют один интерфейс: подпись формы считается из отсортированного списка полей и их типов.
Генерация типов и другие способы описать данные
Генератор по JSON удобен тем, что берёт готовое и правдоподобное описание. Но у него есть альтернативы, и у каждой своя сильная сторона. Написать типы руками можно, и это даже лучше: вы сразу подумаете о необязательных полях, о форматах дат и о том, какие значения вообще допустимы. Цена этого времени, а для рукописных описаний время дороже, чем секунды.
Схема JSON Schema описывает данные формально, проверяет их и работает на разных языках, но требует отдельного файла и своей структуры. Генератор типов из схемы полезен, когда схема уже есть. Третий вариант, самый строгий, это проверка типов во время выполнения через Zod или аналоги, где одно описание отвечает и за типы, и за валидацию. Тогда ответ API сначала проверяется, и только потом попадает в код, но это уже полноценная библиотека, а не страница в браузере.
Отдельно стоит сказать про конвертацию в другие форматы, потому что JSON часто приходит не в том виде, в котором его хочется видеть. В YAML он становится читаемее, в CSV удобнее разбирать табличные данные, а в JSON Schema превращается в формальное описание, по которому ответ можно валидировать. Для работы с запросами внутри JSON пригодится поиск по пути JSON.
Сравнение способов описать данные
| Подход | Плюсы | Чем приходится платить |
|---|---|---|
| Генерация из JSON | Быстро и всегда по факту | Обязательные поля, неверные имена, нет дат |
| Ручное описание | Полный контроль над типами | Время на набор каждого поля |
| JSON Schema | Формально и проверяемо | Отдельный файл и синтаксис схем |
| Схема как источник | Один источник правды | Схему тоже нужно где-то хранить и поддерживать |
| Проверка в рантайме | Ошибка поймана сразу, до использования | Внешняя библиотека и её настройка |
| TypeScript как есть | Ничего лишнего | Слабая гарантия, значения приходят извне |
| Ручная правка генерации | Доработка точечно | Каждое изменение приходится повторять |
Перед тем как описывать тип, посмотрите на сам ответ: форматтер JSON покажет его в читаемом виде, а валидатор схемы проверит соответствие.
Приёмы, которые упрощают работу с типами
Сгенерированный тип это черновик, а не готовый результат. Пара приёмов превращают его в то, что действительно можно положить в проект.
- Сразу задайте имя корневого интерфейса осмысленным, например `User`, вместо значения по умолчанию.
- Пройдитесь по именам интерфейсов и поправьте те, что образованы неудачно, например `Addres`.
- Отметьте знаком вопроса поля, которые иногда отсутствуют в ответе.
- Добавьте `| null` к полям, которые приходят пустыми, иначе тип будет слишком узким.
- Замените тип дат со `string` на `Date`, если в проекте используется объект Date.
- Проверьте, не схлопнулись ли разные по смыслу объекты в один интерфейс: одинаковая форма не значит одинаковый смысл.
- Сохраните полученный тип рядом с тестовой фикстурой, чтобы он не устарел незаметно.
Частые вопросы
Как обрабатываются имена полей?
Что с пустыми массивами и null?
Полезные статьи
Похожие инструменты
Конвертер JSON в CSV
Конвертация массива JSON в формат CSV с автоопределением столбцов
Конвертер JSON в YAML
Конвертация данных JSON в формат YAML с правильными отступами
Конвертер YAML в JSON
Конвертация данных YAML в формат JSON с поддержкой вложенных структур
Конвертер Markdown в HTML
Конвертация Markdown в HTML с поддержкой предпросмотра
Конвертер HTML в Markdown
Конвертация HTML в формат Markdown с правильной обработкой элементов
Форматирование SQL
Форматирование и украшение SQL-запросов с правильным регистром ключевых слов и отступами
Минификатор JS / CSS / HTML
Минификация JavaScript, CSS и HTML для уменьшения размера файлов со статистикой сжатия
Поиск путей JSON
Находите пути к значениям и ключам в объектах JSON с мощным поиском