ArkType — более быстрая альтернатива Zod
Сравните синтаксис ArkType и Zod, вывод типов, скорость проверки и обработку ответов API. Узнайте, когда выбрать ArkType, а когда остаться с Zod.
ArkType — это валидатор времени выполнения для TypeScript. Схемы в нём записываются строками в стиле TypeScript и компилируются в оптимизированные валидаторы. Команде со стабильной кодовой базой на Zod полная миграция редко оправдывает затраты. А вот для нового проекта или горячего пути валидации ArkType стоит попробовать.
Если вы работаете с Zod, то наверняка видели график бенчмарков ArkType и задавались вопросом, стоит ли такая скорость изучения нового синтаксиса.
В этой статье мы переносим одну схему User с Zod на ArkType. Разберём вывод типов, производительность, валидацию ответа API и компромиссы, а затем прямо скажем, когда лучше остаться на Zod. В примерах используются ArkType 2.2 и Zod 4.
Ключевые выводы
- ArkType описывает схемы строками в стиле TypeScript, поэтому
"'android' | 'ios'"читается в точности как union-тип, который получается на выходе. В Zod то же самое записывается какz.enum(["android", "ios"]). - При вызове типа ArkType на неизвестных данных возвращается либо провалидированное значение, либо экземпляр
ArkErrors, поэтому идиоматичная проверка выглядит какout instanceof type.errors. - На главной странице ArkType заявлено, что в рантайме он в 20 раз быстрее Zod 4. Это собственные данные разработчика, а в Zod 4.5 с тех пор появился
z.compile()для горячих путей. - ArkType 2.2 принимает внутри
type()любой валидатор, реализующий Standard Schema, поэтому существующую схему Zod 4 можно вложить в определение ArkType, не переписывая её заранее.
В чём главное отличие ArkType от Zod?
Zod строит схему из цепочки вызовов методов, а ArkType записывает её строками в стиле TypeScript, так что схема читается как тип, который из неё получается. Поле, объявленное как "(number | string)[]", — это та же аннотация TypeScript, которую вы бы написали в любом случае, только в кавычках. В руководстве ArkType «Your First Type» отмечается, что редактор проверяет такие строковые определения прямо по мере ввода, с автодополнением, используя систему типов самого TypeScript.
Одна и та же схема User в Zod и ArkType
Ниже приведена одна и та же схема из трёх полей в обеих библиотеках: обязательная строка, объединение из двух значений и необязательный массив чисел или строк.
Zod 4:
import * as z from "zod"
const User = z.object({
name: z.string(),
platform: z.enum(["android", "ios"]),
versions: z.array(z.union([z.number(), z.string()])).optional(),
})
ArkType 2.2:
import { type } from "arktype"
const User = type({
name: "string",
platform: "'android' | 'ios'",
"versions?": "(number | string)[]",
})
В версии на ArkType нет вложенных вызовов билдеров. Меняется и способ указания необязательности: ArkType помечает ? ключ, как это делает TypeScript, тогда как Zod вызывает .optional() у значения. Кроме того, ArkType поддерживает глобальную настройку exactOptionalPropertyTypes (добавлена в 2.1.12), которая соответствует одноимённой опции компилятора TypeScript.
| Критерий | Zod 4 | ArkType 2.2 |
|---|---|---|
| Синтаксис схемы | Цепочки методов-билдеров | Строки в стиле TypeScript и объектные литералы |
| Необязательное поле | .optional() у значения | "key?" у ключа |
| Статический тип | z.infer<typeof User> | typeof User.infer |
| Валидация неизвестных данных | User.safeParse(data) | User(data) |
| Проверка на ошибку | !result.success | out instanceof type.errors |
| Читаемый текст ошибки | Собирается из result.error.issues | out.summary |
| Компиляция | Включается вручную через z.compile() (Zod 4.5+) | Встроена в обработку определений |
Вывод типов: typeof User.infer и z.infer
Обе библиотеки выводят статический тип из схемы времени выполнения, поэтому писать интерфейс вручную не нужно. Отличается только синтаксис извлечения типа.
// Zod
type User = z.infer<typeof User>
// ArkType
type User = typeof User.infer
В Zod z.infer — это обобщённая утилита, которая применяется к типу схемы. В ArkType infer — это свойство самого типа, к которому обращаются через typeof. Для схемы выше оба варианта дают одну и ту же структуру: { name: string; platform: "android" | "ios"; versions?: (number | string)[] }.
Действительно ли ArkType быстрее Zod?
ArkType заранее, при создании каждого Type, строит оптимизированный валидатор. В документации по конфигурации ArkType описан этот этап предварительной компиляции и опция jitless, которая его отключает. На главной странице ArkType утверждается, что в рантайме ArkType в 20 раз быстрее Zod 4. Это результат бенчмарка от самого разработчика, а не независимое измерение.
С тех пор Zod 4.5 частично сократил этот разрыв благодаря z.compile(). Как поясняется в документации Zod по компиляции, z.compile() проходит по схеме один раз и генерирует линейную функцию проверки, которую Zod выполняет через new Function(). Если входные данные не проходят эту быструю проверку, Zod передаёт их обычному парсеру, так что подробные сообщения об ошибках остаются прежними. В README Zod сообщается о медианном ускорении в 2,4 раза на бенчмарке из 55 схем. Поэтому сравнение с некомпилированным Zod 4 ничего не говорит о том, как ArkType покажет себя против скомпилированной схемы Zod.
Для валидации форм разница в скорости между ArkType и Zod редко имеет значение. Несколько проверок на одно действие пользователя не станут узким местом. Скорость начинает играть роль, когда одна схема выполняется тысячи раз в секунду, например в обработчиках запросов или при пакетном импорте.
Сквозная валидация ответа API
Самая распространённая практическая задача — проверить ответ fetch, прежде чем остальное приложение начнёт ему доверять. Вот одна и та же функция в обеих библиотеках.
ArkType:
async function getUser(id: string) {
const res = await fetch(`/api/users/${id}`)
const out = User(await res.json())
if (out instanceof type.errors) {
console.error(out.summary)
return null
}
return out // typed as User
}
Zod:
async function getUser(id: string) {
const res = await fetch(`/api/users/${id}`)
const result = User.safeParse(await res.json())
if (!result.success) {
console.error(result.error.issues)
return null
}
return result.data
}
Zod и ArkType возвращают результат валидации в разной форме. safeParse в Zod возвращает размеченный объект результата с полями success, data и error. Тип ArkType возвращает либо непосредственно провалидированное значение, либо экземпляр ArkErrors. После проверки instanceof TypeScript сужает тип out до User. out.summary — это одно читаемое сообщение, в котором перечислены все невалидные пути, что ожидалось в каждом из них и что было получено.
Кроме того, ArkErrors можно передавать напрямую в JSON.stringify(). Это изменение появилось в ArkType 2.1.10 и упомянуто в примечаниях к релизу 2.2. Благодаря этому ошибку валидации можно сразу отправить в ответ API или записать в лог, не создавая предварительно форматтер.
Компромисс: экосистема и привычность против новой грамматики
Главное преимущество Zod перед ArkType — всё, что его окружает. У него обширная экосистема интеграций, а большинство TypeScript-разработчиков уже умеют читать схемы Zod. Часть этого разрыва сокращается благодаря Standard Schema — общему интерфейсу, который реализуют Zod, ArkType и Valibot. Перед переходом проверьте, поддерживают ли Standard Schema ваша библиотека форм, роутер и RPC-слой.
У ArkType есть вполне реальные издержки:
- Новая грамматика. Объединения, массивы и необязательные ключи выглядят привычно, но ограничения и более сложные выражения — это строковый язык, который команде придётся освоить.
- Ошибки проявляются как ошибки типов в строках. Опечатку вроде
"strng"TypeScript поймает прямо в редакторе, но это будет ошибка в строковом определении, а не отсутствующий метод, так что команде придётся привыкнуть к сообщениям об ошибках нового вида.
ArkType 2.2 позволяет внедрять библиотеку постепенно. В документации ArkType по интеграциям показано, что type() принимает любой валидатор Standard Schema — как отдельно, так и внутри объектного определения — и выводит и проверяет его как нативное определение ArkType. Это значит, что существующие схемы Zod 4 можно оставить без изменений:
import * as z from "zod"
import { type } from "arktype"
const ZodDevice = z.object({ platform: z.enum(["android", "ios"]) })
const User = type({
name: "string",
device: ZodDevice, // Standard Schema validator nested in ArkType (2.1.28+)
})
Если главное ограничение для вас — не скорость, а размер бандла, присмотритесь к Valibot.
Когда стоит переходить с Zod на ArkType?
Большинству команд со стабильной кодовой базой на Zod и работающими интеграциями переходить на ArkType пока не стоит. Затраты на миграцию превышают выигрыш в скорости, который компилятор Zod 4.5 уже частично покрывает. Попробуйте ArkType, если:
- Вы начинаете новый проект, а ваш инструментарий поддерживает Standard Schema.
- У вас есть подтверждённый горячий путь, например высоконагруженный эндпоинт или пакетная задача, где профилирование показывает заметные затраты на валидацию.
- Вы хотите опробовать его на одном эндпоинте, вкладывая существующие схемы Zod в определения ArkType вместо того, чтобы их переписывать.
В остальных случаях продолжайте валидировать данные с помощью Zod и попробуйте z.compile() для схем, которые выполняются чаще всего.
Заключение
Главное преимущество ArkType — читаемость: схема выглядит как тип, который из неё получается, и при этом вы получаете быстрый скомпилированный валидатор. Преимущество Zod в том, что он уже встроен в ваш стек. Чтобы недорого проверить разницу, выберите один эндпоинт с интенсивной валидацией, опишите его на ArkType 2.2, вложив существующие схемы Zod, и сравните его профилирование с версией той же схемы Zod на z.compile(), прежде чем менять что-либо ещё.
Часто задаваемые вопросы
Работает ли ArkType в Cloudflare Workers или при строгой Content Security Policy?
Да. ArkType предварительно компилирует логику валидации через new Function при создании Type и автоматически отключает это в средах, где new Function не поддерживается, например в Cloudflare Workers. При CSP без 'unsafe-eval' установите опцию jitless в true. Настройте её из 'arktype/config' до импорта чего-либо из 'arktype', чтобы встроенные ключевые слова её учли. Валидация при этом продолжает работать, просто без предварительно скомпилированных валидаторов.
Может ли ArkType генерировать JSON Schema, как Zod 4?
Да. У каждого Type в ArkType есть метод toJsonSchema(), а в ArkType 2.2 добавлен пакет @ark/json-schema для обратного направления — он преобразует JSON Schema в типы ArkType. Возможности, не имеющие аналога в JSON Schema, например морфы, ключи-символы или Date, по умолчанию приводят к выбросу исключения в toJsonSchema(), а опция fallback позволяет обработать каждый такой случай. В Zod 4 та же задача решается с помощью z.toJSONSchema().
Что в ArkType является аналогом transform() из Zod?
В ArkType преобразования называются морфами и подключаются через .pipe(), например type('string').pipe(s => s.trim()). Встроенные ключевые слова для парсинга, такие как 'string.json.parse' и 'string.numeric.parse', выполняют распространённые преобразования без колбэка. Если морф выбрасывает исключение, ArkType считает, что вы намеренно хотели аварийно завершить выполнение. Чтобы вместо этого превратить выброшенное исключение в результат ArkErrors, используйте .pipe.try(). Выведенный тип отражает результат морфа.
Может ли ArkType выбрасывать исключение при невалидных данных, как parse() в Zod, вместо возврата ошибок?
Да. Вызов out.throw() у результата ArkErrors выбрасывает его как исключение, а глобальная опция onFail заставляет каждый Type выбрасывать исключение при невалидных данных: configure({ onFail: errors => errors.throw() }) из 'arktype/config'. Объявите тот же onFail в глобальном интерфейсе ArkEnv, чтобы TypeScript знал, что вызовы больше не возвращают ArkErrors. Стиль с возвратом значения по умолчанию соответствует safeParse() в Zod, а onFail — parse().
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k