Кратко: это максимально безопасный по типизации инструмент, генерирует статические файлы с кодом для максимальной совместимости, быстрый благодаря предварительной компиляции (AOT) и компактный. Также он предоставляет удобный генератор модульных тестов, чтобы можно было быть почти на 100% уверенным, что создаваемые предикаты работают, как ожидается.

Этот документ предлагает детальное сравнение Generator с другими инструментами для проверки типов во время выполнения, а также дает более широкий обзор смежных тем. Со временем он превращается в более аналитическую статью, а не просто "мой X круче, чем ваш Y".

Введение

Прежде всего, выражаю искреннее уважение всем разработчикам инструментов, особенно тем, кто участвует в проектировании, реализации, тестировании и документировании каждого инструмента, с которым я сравниваю Generator в этом посте (zod, typia, ts-auto-guard, ts-runtime-checks). Продолжайте отличную работу, друзья!

Сравнение с чистыми проверщиками типов во время выполнения

Самая популярная библиотека в этой категории — это известная zod.

Проверщики типов в этой категории используют только код, который выполняется движком JavaScript в приложении. Типовая информация не используется в этих библиотеках. Некоторые из них, например, используют eval для преобразования схемы в JS-код во время выполнения, другие, такие как zod, применяют чистую композицию функций.

Основные преимущества этого подхода — простота использования и гибкость в выражении правил (в конце концов, это построено на основе тьюринг-полного JS). Этот подход также относительно проще развивать, поскольку он использует только JS/TS без сложного взаимодействия с API TypeScript.

Теперь перейдем к ключевым отличиям Generator по сравнению с чистыми проверщиками типов во время выполнения.

Generator генерирует код, который работает более чем в 100 раз быстрее

Ознакомьтесь с результатами тестов производительности.

Основная причина заключается в том, что Generator генерирует специализированный код, который современные JS-движки легко оптимизируют. Каждая функция-предикат для типов состоит из простых инструкций (например, typeof x === "string" и x === "constant"), которые в большинстве случаев даже не вызывают других функций и никогда — внешних или общих функций. JIT-движки JavaScript предпочитают, когда типы локальны и для каждого отдельного типа создается отдельная небольшая функция. Это позволяет JS-движкам специализировать эти маленькие функции во время выполнения. Подробнее об этом можно прочитать в этой удивительной статье, где объясняется, как V8 обрабатывает полиморфные функции.

Чистые проверщики типов, такие как zod, используют вместо этого композицию функций. В этом случае базовые функции (например, hasProperty(object, propertyName)) многократно используются с различными типами значений и, соответственно, с различными скрытыми классами. Это приводит к частым деоптимизациям (падению до более медленного не-оптимизированного байткода вместо более быстрого нативного кода). В самых тяжелых случаях JIT-компилятор может чередовать оптимизацию и деоптимизацию для одного типа, затем для другого, затрачивая множество циклов на компиляцию вместо выполнения.

У Generator нет зависимостей во время выполнения

Код, который добавляется в итоговый бандл, — это именно тот код, который вы видите в выводе git diff вашего проекта после генерации предикатов типов. Единственные дополнительные функции, которые используют предикаты, — это встроенные методы, такие как Array.isArray() (причем они безопасно обернуты!). Это означает, что размер бандла, который добавляется Generator для предикатов, остается минимальным. Он растет линейно с увеличением размеров типов. Количество кода, необходимое для определения типа Zod, сопоставимо с размером среднего предиката после минификации.

Чисто runtime-библиотеки для проверки типов по своей природе являются зависимостями во время выполнения. Они требуют включения библиотеки матчеров во время выполнения в итоговый бандл. Большинство библиотек используют удобные конструкторы, которые достаточно гибки, чтобы удовлетворить большинство требований общего API. Это хорошо, но увеличивает размер бандла (60+ КБ для zod), поскольку более обобщенные матчеры не могут быть статически tree-shaked (вот интересная попытка уменьшить размер бандла).

Еще одна проблема, которая возникает с исключительно runtime-библиотеками, используемыми в производительно- и доступно-критичных частях приложения, — это совместимость на этапе компиляции с рантаймами WinterCG. Тяжелые runtime-библиотеки, как правило, ориентированы на богатые функционалом среды, такие как Node.js, и добавляют обработку крайних случаев (например, генерацию сообщений об ошибках), требуя дополнительного тестирования. Это не является проблемой, если вы работаете на широко поддерживаемых платформах, таких как Node.js или последние версии браузеров. Однако, если вы, например, используете Wasmer Edge, добавление на первый взгляд тривиальной зависимости может превратиться в серьезную проблему.

Generator создает строго типобезопасный TypeScript-код

Это одно из ключевых отличий Generator. Код, созданный Generator и попадающий в ваш бандл, сначала проверяется вашей настройкой TypeScript, чтобы убедиться, что он безопасен внутри и соответствует проверяемым типам. Generator отлично справляется с этим, создавая строго типобезопасный код, который будет компилироваться даже при самых строгих конфигурациях TypeScript. Кроме того, если вы вносите изменения в тип, для которого был сгенерирован предикат, tsc напомнит вам обновить предикат (просто повторно его сгенерировав).

Чисто runtime-библиотеки не могут напрямую использовать мощь TypeScript для проверки, что составленная функция включает все необходимые проверки в правильном порядке. Даже корректный TypeScript-предикат типов, который не использует хотя бы оператор типа satisfies, может легко обмануть себя, просто возвращая true для любого входного значения без достаточных проверок.

function isUser(value: unknown): value is User {
  return true; // TypeScript слепо доверяет нам здесь
}

Конечно, такие готовые к продакшену библиотеки, как zod, используют TypeScript для проверки корректности собственного кода и предоставляют вспомогательные функции для вывода типов из runtime-блоков. Это значительно повышает надежность кода по сравнению с чисто JavaScript-библиотеками. Но даже это не позволяет вашему проекту полностью полагаться на tsc для проверки корректности итогового кода. Всегда остается некоторая часть оберток, связующего кода или вспомогательных функций, которые не могут быть проверены.

Generator генерирует читаемый и модифицируемый код

В случаях, когда возникает критическая проблема, не поддерживаемая сгенерированным предикатом, или обнаружен баг, требующий срочного исправления, код, созданный Generator, остается читаемым и может быть немедленно отредактирован. Поскольку код предикатов типов редко изменяется, необходимость отправлять патчи в основной репозиторий Generator отсутствует (хотя это, конечно, приветствуется!). Такое исправление будет тривиальным для ревью в рамках небольшого PR и останется локальным для конкретного предиката, что позволяет команде решить проблему без необходимости ожидать обновлений в процессе разработки Generator.

В случае использования runtime-библиотеки исправление потребует внесения патча непосредственно в библиотеку для устранения проблемы. Такой патч затронет весь код, проверяемый библиотекой, и потребует более тщательного тестирования. Временный форк библиотеки может быть решением, но это потребует изучения процесса сборки и публикации данной библиотеки.

Generator не требует определения типов через собственный DSL

Generator работает с любым типом, определенным в любой части приложения с использованием только нативного TypeScript. Это может быть тип из сторонней библиотеки или публичный тип другой команды, не использующий runtime-библиотеки для проверки типов. Все это просто типы, которые совместимы и компонуются.

Напротив, все runtime-библиотеки по своей природе предоставляют набор классов или функций для создания экземпляра проверщика типов, который используется для проверки значений. Большинство из них требует вывода итогового типа из составной функции. Это фактически превращает runtime-библиотеки в DSL-ориентированные инструменты (популярные, например, в мире Ruby) вместо того, чтобы быть по-настоящему TypeScript-first инструментами. В результате акцент смещается в сторону подхода, ориентированного на схемы, где TypeScript становится мощным вторичным инструментом, а не основной целью.

Generator создает код, который значительно быстрее стартует с нуля

Это предусмотрено архитектурой: код, генерируемый Generator, изначально более производителен для обработки JS-движками. Современные движки JavaScript используют множество трюков для ускорения начального парсинга, включая ленивый парсинг. Код, созданный Generator, является нативным для таких оптимизаций, так как он ничем не отличается от любого другого кода продакшн-приложения. Его также тривиально tree-shake, что помогает исключать неиспользуемые части из бандла.

Подход runtime-библиотек по своей природе требует запуска некоторого кода на этапе старта приложения, который заставляет JS-движок парсить, компилировать и выполнять код библиотеки, даже если предикат не будет сразу использоваться. Для смягчения этой проблемы можно обернуть runtime-библиотеки в фабричные функции, но их код всё равно попадет в бандл и будет обработан на старте.

Стоит отметить, что существуют JavaScript-рантаймы, которые используют снимки кучи (V8 snapshots, JS-движки на основе WASM). Они позволяют сделать снимок памяти JS-движка после инициализации приложения. Если это напоминает вам preforking-сетевой демон, то аналогия действительно близка. Эти среды могут значительно ускорить холодный старт для runtime-библиотек, но ценой увеличения использования памяти, так как итоговая функция-проверка должна быть создана в куче, даже если она не используется.

Generator не использует eval()

Generator компилирует код предикатов на этапе сборки и не использует eval() и подобные методы, которые недоступны в высокопроизводительных и безопасных рантаймах, таких как Vercel Edge Runtime, AWS CloudFront, Cloudflare Workers и Akamai EdgeWorkers.

Runtime-библиотеки, которые используют eval() и аналогичные методы для реализации JIT-оптимизаций, добавляют дополнительные шаги, которые JS-движок должен выполнить перед запуском фактического кода предиката типов. Подход с eval() требует от библиотеки создания исходного кода предиката в виде строки, которая обычно составляется из меньших строк. Эти строки затем подлежат сборке мусора, передаются движку для парсинга и только потом исполняются. Несмотря на гибкость, этот метод вносит дополнительную задержку при старте, увеличивает потребление памяти и требует больше ресурсов от среды выполнения.

Стоит также отметить, что использование eval() является потенциальной проблемой безопасности. Результирующий артефакт может быть классифицирован инструментами аудита безопасности как потенциально уязвимый. Честно говоря, в случае библиотеки проверки типов это не должно быть реальной проблемой, особенно если исходный код библиотеки регулярно проверяется и поступает из надежного источника.

Упрощённая отладка и улучшенные стеки вызовов

Каждая библиотека время от времени вводит или обнаруживает баги. В случае с Generator, исходный код, который может вызывать исключения, явно включён в ваш бандл и снабжён исходными картами (source maps). Использование пошагового отладчика для сгенерированного кода ничем не отличается от отладки вашего собственного кода. Стек вызовов в инструменте отчётов об ошибках также будет кристально понятным. Хотя баги в коде, подобном тому, что создаёт Generator, случаются редко, они всё же возможны. Например, когда Proxy выбрасывает исключение при доступе к свойству, getter неожиданно возвращает null, или библиотека неправильно использует изменение глобальных prototype.

В случае runtime-библиотек стек вызовов чаще всего содержит минифицированные символы. Это особенно заметно при использовании новой нативной поддержки TypeScript в Node.js, где исходные карты вообще не используются.

Нативный опыт работы в IDE

Код, сгенерированный Generator, тривиально доступен через функцию "перейти к определению" в любой IDE. Типы при наведении мыши — это те же самые типы, которые используются в вашем приложении, без дополнительных обёрток или переименований. Более того, сам код предиката типов доступен всего в один клик, если потребуется его модификация.

В случае с zod вы увидите выведенные типы, которые не могут иметь других удобных возможностей, таких как JSDoc на уровне типов (JSDoc на уровне свойств работает, но ограниченно) или generics на уровне типов (существуют обходные решения на уровне рантайма, но они требуют усилий для реализации).

Generator produces code that is easy to review

Одним из преимуществ явной генерации кода, хранящегося в системе контроля версий вашего репозитория, является его простота аудита. Все изменения становятся сразу видимыми во время ревью PR через инструменты вроде GitHub-based сканеров кода. Это также упрощает обновление пакета Generator: все изменения, которые новая версия вносит в сгенерированный код, сразу видны в git diff проекта и покрываются сгенерированными unit-тестами.

Для полноты обсуждения вопроса безопасности, включая немного безопасность цепочки поставок, стоит отметить, что пользователи runtime-библиотек всё равно запускают скомпилированный в JavaScript код рантайм-чекера, который теоретически может содержать что угодно, так как файлы *.d.ts не предоставляют посткомпиляционной проверки для *.js-кода, входящего в бандл библиотеки. Поэтому для более строгих в плане безопасности приложений возможность проверки фактического кода средствами безопасности проекта может быть важной.

Сравнение с плагинами компилятора TypeScript

Наиболее популярным и функционально насыщенным инструментом в этой категории является Typia.

Типовые проверки в этой категории генерируют код предикатов типов на этапе сборки. Они подключаются к компилятору tsc в качестве плагинов и предоставляют вспомогательные типы, которые на этапе генерации кода транспилируются в фактический JavaScript-код в процессе компиляции с использованием tsc.

Основные сильные стороны инструментов в данной категории заключаются в их удобстве использования (достаточно добавить что-то вроде is, и это магически начинает работать) и поддержке практически всех типов, которые предлагает TypeScript. Эта мощь исходит из того, что плагин компилятора по сути является плагином: он может делать всё, что способен компилятор tsc.

Generator does not require tsc plugins

Generator — это автономный инструмент, который включает собственную версию TypeScript, не влияющую на настройку TypeScript в вашем приложении. Даже если Generator сломается при обновлении, сгенерированный им код предикатов уже будет сохранён в вашем репозитории и не исчезнет. Код предикатов довольно статичен и не требует обязательного присутствия Generator в процессе сборки; вы можете просто использовать код из Playground.

Так как TypeScript официально не поддерживает плагины для tsc, проверяющие библиотеки, которые зависят от подключения к tsc, требуют патчей для самого tsc. Это становится блокером для большинства команд, которые хотят использовать чистый компилятор TypeScript по разным причинам. Основной причиной является надёжность, так как TS является сеткой безопасности, которая гарантирует, что код будет работать в продакшн-среде.

Ещё одной проблемой использования плагинов для tsc является то, что крупные обновления TypeScript часто вносят изменения, нарушающие совместимость с внутренними API. В оптимистичном случае это просто ломает плагины, и они выдают ошибку. В более сложных случаях плагин продолжит работать, но может генерировать некорректный код. Это, в самом строгом смысле, требует от разработчика приложения подождать, пока все плагины обновят свою поддержку TypeScript, обновят тесты и опубликуют новые версии пакетов. В проектах с жёсткими SLA это может потребовать дополнительного времени, пока сообщество адаптирует новые версии пакетов.

Я должен признать, что плагины tsc предоставляют элегантные API, которые мне лично нравятся как энтузиасту языка программирования. Но, строго говоря, плагины TypeScript расширяют сам язык в целом и не соответствуют курсу, который TypeScript и остальное сообщество (см. рассуждения и комментарии здесь) придерживаются уже долгое время. Немного грустно, но в противном случае мы бы уже получили ещё один форк JavaScript, как CoffeeScript или Flow от Facebook.

Генератор генерирует типобезопасный TypeScript

Как упомянуто выше, Генератор генерирует TypeScript код, который является строго типобезопасным и проверяется с помощью вашей настройки tsc в соответствии с правилами безопасности вашего проекта.

Плагины TS, с другой стороны, генерируют JavaScript код, который не проверяется компилятором TypeScript, что означает, что эта часть кода, которая выполняется в вашем проекте, остаётся чистым JavaScript, даже если она исходит от компилятора TypeScript. Конечно, создатели инструментов хорошо тестируют выводимый код, но вам всё равно нужно делегировать безопасность типов на внешний инструмент в процессе сборки.

Код Генератора явно читаемый и изменяемый

Как упомянуто выше, Генератор генерирует код, который читаемый и изменяемый.

Чтобы проверить, что на самом деле генерирует проверяющий на основе плагина tsc, вам нужно будет извлечь сгенерированный код из процесса компиляции. Этот код представляет собой чистый JavaScript, что требует вручную проверить корректность сгенерированного кода.

Тот факт, что сгенерированный код в большинстве настроек не проверяется в репозитории, делает процесс обновления пакетов многоступенчатым. Чтобы убедиться, что новая версия проверяющего на основе плагина генерирует сопоставимый код, вам нужно будет где-то сохранить текущий сгенерированный код, обновить пакет и выполнить дифф. С Генератором это работает из коробки.

Единственный недостаток того, что Генератор генерирует файлы — это явный шаг сборки. С плагином tsc не нужно беспокоиться о коде; плагин всегда поддерживает предикаты в актуальном состоянии. Тем не менее, не все инструменты сборки и разработки поддерживают горячую перезагрузку с полной поддержкой плагинов tsc (например, проблема, решение которой заняло почти год).

Код Генератора легко делиться между проектами

Это всего лишь маленький TS файл, который любой инструмент из экосистемы JS может использовать как есть, вместе с остальным кодом вашего приложения.

Если вам нужно было бы поделиться исходным кодом TypeScript, использующим плагины tsc, с другими командами, вам нужно сначала его опубликовать. Это требует компиляции кода в JavaScript-обертку и публикации её вместе с файлами *.d.ts. Другой вариант — заставить другие команды использовать тот же набор и версии плагинов tsc, включая плагин для проверок в их настройках. Это требует координации и может замедлить миграции на новые версии TypeScript. Еще одной преградой могут быть команды, которые не используют tsc для сборки проекта, а вместо этого применяют новые инструменты, которые нативно понимают синтаксис TypeScript и не требуют отдельного шага сборки (Поддержка нативного TS в Node.js, esbuild, Cloudflare wrangler).

Сгенерированный код Генератора легко отлаживать

Как упомянуто выше в разделе Легкость отладки и лучшие трассировки стека, Генератор генерирует код, который дружелюбен к отладчикам и инструментам отчетности об ошибках. Трассировки стека соответствуют вашему приложению, давая мгновенную обратную связь о том, откуда возникла ошибка (как ошибки валидации, так и потенциальные ошибки на этапе выполнения).

В случае с плагином tsc при отладке и чтении трассировок стека карты исходников будут указывать только на один символ в исходном коде TS (в большинстве случаев это токен is). В случае проблемы или ошибки в исходном коде или самом проверяющем плагине потребуется инспектировать сам исходный JavaScript-бандл.

Однако существует обходной путь. Должно быть возможно опубликовать как карты исходников для бандла вашего приложения, так и для зависимостей, и настроить инструмент отчетности об ошибках на поиск этих карт исходников там. Быстрое гугление не дало четких результатов о том, как работать с транзитивными картами исходников.

Генератор поддерживает все инструменты JS/TS

Код предикатов легко использовать с новой функцией TypeScript для Node.js. Нет необходимости использовать ts-node или предварительно компилировать код с помощью tsc. То же самое относится и к различным тестовым раннерам и сборщикам для edge-окружений.

С плагином tsc вы ограничены инфраструктурой, ориентированной на tsc. Это не слишком сложно, но может потребовать дополнительной настройки и ухудшить производительность. Например, Vite компилирует файлы *.ts в 20-30 раз быстрее с использованием компилятора по умолчанию esbuild, чем с tsc. Новый супербыстрый пакет ts-blank-space даже не выполняет анализ типов TypeScript, необходимый для работы плагинов tsc, и, благодаря этому, работает очень быстро.

Итак, еще раз, расширение системы типов, которое влияет на синтаксис, эффективно превращает эту категорию проверок в расширение языка.

Сравнение с другими генераторами кода

Самым полным (и дорогим для меня, поскольку я использовал его в прошлом) инструментом в этой категории является ts-auto-guard.

Эта категория проверок генерирует итоговый код предикатов как отдельные статические файлы, которые должны быть явно импортированы в код приложения и собраны с остальной частью кода. Генераторы попадают в эту категорию рантайм-проверок типов.

Большинство аргументов, приведенных выше в пользу использования Генератора, в той или иной мере применимы ко всем инструментам в этой категории. Основные преимущества этого класса инструментов — совместимость и предсказуемость. Это хорошо перекликается с подходом к инструментам, который принят в более прагматичных экосистемах, таких как Golang, разработка компиляторов и тому подобное.

Генератор генерирует типобезопасный TS

Как уже упоминалось выше, Генератор генерирует TypeScript код, который строго типобезопасен.

Другие генераторы кода, также производящие TypeScript, генерируют код, который не является типобезопасным. Неопасно сгенерированный код использует небезопасные конструкции, такие как приведение типов (с помощью as), или может полагаться на небезопасные типы (чаще всего any). Это фактически превращает сгенерированный TypeScript код в слабо типизированный JavaScript. Если конфигурация вашего приложения использует строгие линтеры TypeScript, такой код потребует добавления исключений и снизит общий уровень типовой безопасности.

Генератор генерирует читаемый код

Как уже упоминалось, Генератор генерирует код, который легко читать и изменять. В дополнение к этому, по сравнению с другими инструментами генерации кода, Генератор помогает вам с чтением, отладкой и, возможно, ручным изменением сгенерированного кода, линейно структурируя все проверки. Он делает маленькие шаги, которые легко отслеживать, и добавляет исчерпывающие комментарии (скоро!). Это означает, что сгенерированный код не минимизирован, но при этом организован таким образом, что обычные сборщики могут легко превратить его в маленькое объединённое выражение if. Генератор также старается использовать осмысленные имена локальных переменных и временных типов, где это возможно, чтобы улучшить читаемость.

Код, сгенерированный большинством других инструментов в этой категории, предварительно оптимизирован, и потому его не так легко читать. Это дает больший контроль разработчику инструмента для достижения лучшей производительности, так как не все сборщики могут правильно обрабатывать достаточно сложные функции предикатов для более сложных типов.

Генератор всегда генерирует правильный код

Так как код, который генерирует Генератор, строго проверяется компилятором TypeScript и также генерируется с использованием API TS AST-строителя (посмотрите замечательный ts-ast-viewer.com для работы с API), Генератор не может сгенерировать некорректный TypeScript код.

Во время моих исследований я обнаружил, что для некоторых нетривиальных случаев другие инструменты, использующие конкатенацию строк, могут генерировать некорректный или неполный код. К счастью, некорректный код сразу отклоняется компилятором TypeScript (это супервозможность инструментов генерации кода в целом), но неполнота кода не покрывается, так как сгенерированный код не является типобезопасным. Это также касается инструментов, которые зависят от eval() во время выполнения.

Код Генератора быстрый

Маленькое замечание. Большинство генераторов кода на основе типов показывают значительно (на 100x) лучшую производительность, чем решения, работающие только во время выполнения, и значительно лучшие времена старта по сравнению с теми, которые используют eval. Но у Генератора все же есть несколько фишек.

Что добавляет Генератор, так это однократный доступ к свойствам объекта и широкое использование локальных переменных, что делает код крайне быстрым даже в неоптимизирующих JS-движках (это восходит к техникам, использовавшимся для ускорения старых JS-движков). Также этот трюк потенциально упрощает анализ для современных JS-движков.

Большинство других проверяющих формируют полные или частичные выражения для доступа к вложенным свойствам, что требует времени для анализа и оптимизации движком JS, так как это просто больше байт-кода для анализа:

function isUserProperties(v) {
  // …
  return typeof v.a.b === "object" && v.a.b !== null;
}

// node --print-bytecode --print-bytecode-filter=isUserProperties bytecode.js
// GetNamedProperty a0, [0], [0]
// Star0
// GetNamedProperty r0, [1], [2]
// TestTypeOf #7
// JumpIfFalse [13] (0x22bd65001e68 @ 24)
// GetNamedProperty a0, [0], [0]
// Star0
// GetNamedProperty r0, [1], [4]
// TestNull
// LogicalNot
// Return

function isUserLocals(v) {
  // …
  const b = v.a.b;
  return typeof b === "object" && b !== null;
}

// node --print-bytecode --print-bytecode-filter=isUserLocals bytecode.js
// GetNamedProperty a0, [0], [0]
// Star1
// GetNamedProperty r1, [1], [2]
// Star0
// TestTypeOf #7
// JumpIfFalse [6] (0x5e08e641f02 @ 18)
// Ldar r0
// TestNull
// LogicalNot
// Return

В коде с локальной переменной нет второго GetNamedProperty. Однако используется один дополнительный регистр.

Генератор также тестирует сгенерированный код

В качестве бонуса, во время генерации предикатов, Генератор может также по желанию генерировать набор юнит-тестов для сгенерированных предикатов. Вы можете запускать эти тесты как часть тестового набора вашего приложения.

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

Лучше ли это, чем ChatGPT?

Да, на данный момент это так.

С учетом того, что в последние дни искусственный интеллект создает все больше контента, с Генератором вы можете доверять сгенерированному коду, так как это простейший инструмент, который слишком прост, чтобы создавать галлюцинации. Генератор выдаст ошибку для типов, которые он не может конвертировать, вместо того чтобы генерировать неправильный код и убеждать вас, что он правильный.

Я пробовал Copilot и ChatGPT. Оба инструмента ИИ сгенерировали небезопасный TS, который даже не компилировался без ошибок на первом этапе, несмотря на любые подсказки. В одном из запусков ChatGPT просто сломался посередине генерации кода с набором совершенно не относящихся к делу символов.

И, как стало более очевидно, Генератор намного быстрее любого ИИ-инструмента. Ах, чуть не забыл упомянуть, Генератор также бесплатен.

Резюме

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

Генератор — это инструмент, который очень прост в своей работе, полагаясь на другие инструменты для выполнения остальной тяжелой работы: для генерации кода используется API TypeScript, для минификации используется esbuild с настройками по умолчанию, для проверки типов, конечно же, Генератор делает сгенерированный код строго типизированным и использует оператор satisfies. В дополнение к этому, Генератор дополнительно генерирует набор юнит-тестов для проверки других сгенерированных кодов. Также функциональность минимальна.

Комментарии (8)


  1. nin-jin
    25.01.2025 01:42

    ой, не любые типы
    ой, не любые типы

    https://peter-leonov.github.io/type-predicate-generator/?s=PTAEBUAsEsGdTqAhgO1AUwB5ILYAcAbdUWdAF1AHsAzUMgTz3VgBoAoEUQpe0Ad2hlIdSOhxUATlySwyxepQCuUynxQA6Nmyx5KEigyagAckjLKkBUAF5QAcgB6AHQAmAagAkd0JwD8-7UxdfTpGYgBRHCRoK1sAAw8Ab1kJaBQAcwBfAAEklLSsuK0dPQMw0ABVUilbRLZQBBcALhMzCwIAbnqMKJiWyOjO0DZMrq1xiaA

    $mol_data - runtime без eval, где же 100х?
    $mol_data - runtime без eval, где же 100х?

    https://mol.hyoo.ru/#!section=bench/bench=runtype

    И, наконец, следите за руками:

    const Duration = $mol_data_pipe(
    	$mol_data_variant(
    		$mol_data_string,
    		$mol_data_integer,
    	),
    	$mol_time_duration,
    )
    
    Duration( 'P15D' ).day // 15
    Duration( 1500 ).count( 'PT0.5S' ) // 3
    Duration( Math.PI ) // Error
    Duration( '2025' ) // Error

    https://github.com/hyoo-ru/mam_mol/tree/master/data


    1. JPEG Автор
      25.01.2025 01:42

      Спасибо за фидбек. Зарепортил недостающую поддержку строк: https://github.com/peter-leonov/type-predicate-generator/issues/21

      Остальное я не до конца разобрал, ты наверное торопился. Это еще один сравнительный бенчмарк? Я бы рад добавиться и туда, но они все разные внутри и требуется пол-литра чтобы правильно встроить свой кейс.


      1. JPEG Автор
        25.01.2025 01:42

        Ааа, разобрался, это целый мощный фреймворк с кучей модулей и тонной примеров. Что сказать, респект, не знал про него.


        1. nin-jin
          25.01.2025 01:42

          Дело не в строках, а в том, что не все валидации могут быть вообще описаны типами, а если и могут, то через адские костыли. А кроме валидации нужна ещё и нормализация, с чем типы вообще не справляются.

          По второй ссылке бенчмарк, да. Будет классно, если добавите и свою реализацию.


          1. 0xBAB10
            25.01.2025 01:42

            через сигма типы могут


            1. JPEG Автор
              25.01.2025 01:42

              Звучит круто, это как?


          1. JPEG Автор
            25.01.2025 01:42

            Это да. Но цели заменять тьюринг полный яваскрипт и не стоит. Для более сложных схем есть SDK генераторы.


          1. JPEG Автор
            25.01.2025 01:42

            мне только что повезло выйти на второе место в другом бенче, так что я пока без мотивации добавлять свою тулзу. Если есть время и желание можешь глянуть этот ишшу там два пиара в другой бенчмарк. Если получится большое спасибо!