От JSON к TypeScript: пять способов уйти от ручного написания интерфейсов

Сырые ответы API в JSON редко превращаются в типы TypeScript «одним касанием»: неверно помеченный optional, смешение null и undefined на разных сэмплах или вывод по одному варианту ответа, когда внешний API ведёт себя по-разному — и ошибка всплывает не сразу. Зато инференс и code generation по схеме давно вшиты в рабочий цикл: в одной связке оказываются quicktype, openapi-typescript и Zod, где z.infer связывает валидатор и тип. Практический гайд на Dev.to (автор helloashish99, 25 апреля 2026) разбирает пять приёмов, типичные срывы автогенерации и ситуации, в которых ручная доводка схемы разумнее, чем слепо доверять выходу генератора. Ниже — сжатый пересказ того же материала.
Когда вручную писать типы — это не дисциплина, а источник багов
Копипаст из JSON в ручные интерфейсы в том материале сопоставляют с багами, которые долго не замечают. Перед обзором quicktype, openapi-fetch и Zod автор выделяет три группы ручных ошибок: неверно выставленный optional у поля, которое в реальном ответе исчезает; смешение null и undefined на разных образцах; жёсткий перенос одного варианта ответа, когда API ведёт себя неодинаково. На фоне инференса и code generation это читается так: если слепо довериться сгенерированной сигнатуре, цена ошибки на одном сэмпле оказывается заниженной — отсюда смысл мультисэмпла в quicktype и контрактов вместо разового снимка тела ответа. Время публикации на площадке: 25 апреля 2026, 13:19 UTC. Ориентир длительности чтения на странице Dev.to: 8 минут.
Метод 1 и 2: «раз в браузере» против продакшн-pipeline
Сначала в статье идут браузерные one-off-конвертеры; в примере назван набор на json.renderlog.in для пары JSON→TypeScript, с акцентом на клиентском исполнении. Подчёркнуто, что такой путь не годится для CI и повторяемой регенерации, если нужен одинаковый билд. Дальше — quicktype с npx и мультисэмплом, с обсуждением «шумных» union с null и флагов вроде --no-maps, --no-enums, --no-combine-classes, чтобы сузить форму сгенерированного кода. Словом workhorse в английском вводе окрестили «лошадь» для среды со скриптами в CI / CLI, в отличие от одноразовой вкладки в онлайн-конвертер.
Схема, OpenAPI и openapi-fetch: договор вместо среза ответа
Третий метод — смещение к JSON Schema и OpenAPI: json-schema-to-typescript и openapi-typescript, с аргументом в пользу контракта как источника правды, а не разового снимка тела JSON. Рядом появляется идея типизированных HTTP-клиентов и связок вроде openapi-fetch: типы на границе выводятся из схемы, и разрыв между сгенерированным клиентом и реальными ответами уже не тот, если схема стабильнее, чем снимок, снятый одним curl.
Zod, z.infer и место, где inference сидит в одном кольце с валидацией
Четвёртая ветка — Zod: валидатор на границе (HTTP, формы) и z.infer как совпадение типа с валидатором. Сцепка выглядит так: черновой Zod из JSON через JSON→Zod на json.renderlog.in, с оговоркой, что правила для нетривиальной схемы дальше всё равно дожимать вручную. Здесь вывод типа и проверка данных идут парой: сигнатуру не рисуют отдельно от валидатора — контур типа задаёт сам валидатор. Valibot и Yup в том же сюжете фигурируют как рантайм-альтернативы, без сравнения, которого нет в первоисточнике.
TypeScript → JSON Schema и пятый, обратный путь
Пятый метод — обратный вектор: ts-json-schema-generator, из TypeScript к JSON Schema, если нужно вывернуть модель, оформленную в типах, в схему для других потребителей. Как продукт цепочки TS → JSON Schema это не повторяет квартет приёмов JSON→TS, которые разобрали выше.
Пять мест, где автогенерация умной сигнатуры остаётся с глупостями
Раздел «Five mistakes auto-generation still makes» нумерует пять устойчивых сбоёв. Ниже — смысловой перенос (страница: тот же URL, обращение 25.04.2026, 22:15 UTC).
- Пустые массивы: в стиле
never[]или, хуже,any[], если в образцеroles: []не видно структуры элементов — нужен сэмпл с элементом или доработка, например доstring[]. - Optional и nullable: с одного
{ "email": null }конвейер вправе сделать слишком буквальныйnullвместоstring | null; мультисэмпл снижает риск, но не снимает его полностью. - Discriminated unions: в тексте контраст между плоским интерфейсом с кучей опциональных полей и помеченным union, который сужается в
switch. - Даты-строки: по проводу остаётся
string, на приёме — парсинг; в Zod-цепочке упоминаются.datetime()и.transform. any/unknownв динамичных ветвях: совет — вручную просмотретьanyв сгенерированном файле до коммита.
Инференс типов и автогенерация по схеме — та плоскость, на которой Dev.to подводит читателя к практике: в кратком описании поста рядом фигурируют openapi-typescript и Zod inference, в такт с развёрнутым разбором пяти методов и пяти типичных ошибок.
Масштабирование, крайние случаи и длинный хвост renderlog
Сводный production-sized план в источнике такой: источник правды в схеме, openapi-typescript в CI, рантайм-валидация (Zod / Valibot / Yup) на границе, плюс ручные типы к UI, если у формы другая природа, чем у сетевой (wire) модели. Случаи, когда не стоит автогенерировать, перечислены отдельно: стабильные крохотные payload, тяжело кастомизируемые схемы, одноразовые скрипты, где печать вручную дешевле. Финал возвращается к клиентским вспомогалкам на json.renderlog.in (сравнение JSON, схема из сэмпла, JSONPath, repair, больше языков, токен/JWT) и TL;DR — без детализации renderlog вне приведённого перечня в статье.
Источники
- HelloAshish, From JSON to TypeScript: Five Ways to Stop Hand-Writing Interfaces, Dev.to — Dev.to (дата обращения: 25.04.2026, 22:15 UTC).