Zod 4.5: скомпілявацыя, пераверкі і safeParse: выбір парсера па межах
Як працаваць з тестамі для пераканалка парсероў, скомпіляваных у Zod 4.5, і шляхамі быстрой перацэнкі булевых значэнняў для правамерных і неправамерных пакетаў дадзеных, а таксама як вярнуцься да адпаведных API без выключэння шляхоў для адліквідацыі памылак.
Крыніцы: Zod 4.5 z.compile() Cut My Parse Time. Invalid Payloads Got Faster Than Valid Ones.
Якщо працэўнік маршруту пачынае свою роботу з schema.safeParse(body), за кожны запит вам даводзіцца платіць за аналіз дадзеных за дапамою Zod. У версіі Zod 4.5, аанунцаванай 28 сакавіка 2026 года, дадаўся метод z.compile(), які дазволяе частку ціў работы выкарыстоўваць толькі адназначна, а таксама ёсць швайны спосаб верыфікацыі на базе логічных значэнняў, які прыменшуе неабходнасць стварэння дакладных паведамленняў пра аберанне. Швэйшы аналіз дадзеных корыстны толькі тады, калі інфармацыя пра аберанне, на якую спакоюе ваш интерфейс, застаецца цэлым; таму ў гідзе показана, як вымерыць час выкарыстоўвання трохоў варыянтаў на рэалістычным шэме, з якімі б то не былі вхіднымі дадзенымі, і як выбраць, калькі API падходзіць для кожнага рубежа прыемленае.
Што на самай працэ аанунсавалася ў версіі 4.5
Корыстна адзеліць самыя заявы пра новую версію ад загаворак, якія распрасціваюцца пазней. За інформацыяй з аанунсу версіі 4.5:
- будзь-які шэма можна прадзеўскомпіляваць за дапамою
z.compile(schema), што прымаўляе аналіз дадзеных;
z.validate() — это булевы метод, які працюе швайна, і, як зазначаецца, ён працюе аж у 16 разоў быстрэй з невярнымі дадзенням, таму што не стварае цэлага дрэва проблем;z.string() займаў апошнія 7,5 kB у версіі 4,4 і толькі 784 байты ў версіі 4,5.Спрыяйце гэтым цифрам як показнікам ад разработчыкаў. Фраза «аж у 16 разоў» апісвае найкращы варыянт, таму трэба спрыяваць гэта як напрамак паліпшэння, а не як рэзультат, які трэба чакаць. Эта API ў момент напісання ўсё яшчэ новая, таму пераканайцеся ў назвах і параметрах, суперашчыпваючы іх з актуальной дакументацыяй Zod і вашай версіяю, прычым перш чым паспольгваць чым-небудзь з таго, што напісана нижэй.
Рэалістычны шэма для тэставання
Мікротэсты на адзінам z.string() даюць мало інфармацыі пра аплікацыю. Болей правдзівы тэст выкорыстоўвае схему, якая падобная да рэальнага трафіку: счыт-фактуру з ідэнтыфікатарам, электронным паштою, загальной сумой, валютай, спісам пунктав і неабяжным записам метаданых. Нічога не спрасцавляецца, каб компайлер выглядаў лепей.
import * as z from "zod";
export const Invoice = z.object({
id: z.string().uuid(),
email: z.string().email(),
total: z.number().nonnegative(),
currency: z.enum(["USD", "EUR", "BDT"]),
lines: z
.array(
z.object({
desc: z.string().min(1),
qty: z.number().int().positive(),
unit: z.number().nonnegative(),
}),
)
.min(1),
meta: z.record(z.string(), z.string()).optional(),
});
export const CompiledInvoice = z.compile(Invoice);
export type Invoice = z.infer<typeof Invoice>;
Заўважыце тры рэчі. CompiledInvoice ствараецца толькі адна раз, у масштабе модуля. Выведжаны тип все ж такі выходзіць з аднойчы схемы Invoice, таму што компіляцыя зменяе роботу пад час выканання, а не типы. І оба об’екты экспортуюцца, што, як вы пазнаеце пазней, мае наследкі.
Для вводу выкорыстоўваюце два пакеты дадзеных, якія адпраўляюць тое, што насправды прыме ваша система. Валідны пакет мае тры элементы, валюту BDT і вядомы ключ метадаў. Невалідны пакет аб’еднвае типовыя помылкі корыстувача: total: "na" (клетка табліцы, пераклеўаная у поле з числамі), порожній масэвы элемент lines і email: "not-an-email".
Замер часу работы трох падчынных працоўнікаў
Інструмент павышае актыўнасць кожнага маршруту за дапамойкай 1 000 ітерацыяў, каб JIT адчула стан системы, пасля чаго замервае 10 000 ітерацыяў, чытаючы значэння process.hrtime.bigint() пры пачатку і заканчэнні кожнага циклу, усё гэта вядзецца ў тым жа процесе.
for (let i = 0; i < 10_000; i++) Invoice.safeParse(payload);
for (let i = 0; i < 10_000; i++) CompiledInvoice.parse(payload);
for (let i = 0; i < 10_000; i++) Invoice.validate(payload);
Два застережэння пры копіюванні. Першае — пераканайцеся, якое напісанне викорыстоўваецца для шляху boolean; у фрагменте коду гэта вызначаецца як метод у схеме, тады калі ў анунцыях пісаецца z.validate(). Якщо ваша версія мае толькі safeParse, значыць, у вас не 4.5, і рэзультаты тестаў для версіі 4.4 не павінны прыводзіцца як рэзультаты компіляцыі. Другае — вызов parse выклекчвае аднойчы, калі даходзіць невялікі данні, таму, калі вы вимерваеце час обробкі невялікіх пакетаў, трэба альбо загорнуць гэты цыкл у try/catch, альбо викорыстаць варіянт компіляванага парсера, які не выклекчвае аднойчы; інакша цыкл зупініцца пасля першага невялікі пакета, і ця чысла будуць беззначнымі.
Відтворэнне вимеры
З'яўіце апошню версію і паказваце, якая версія насправды у вас є:
pnpm add zod@latest
pnpm exec node -e "import * as z from 'zod'; console.log('zod', z.VERSION ?? 'read package.json')"
Разместіце схему ў файле src/invoice.ts, а ціклы — у файле src/bench.ts. У Node 24 файлы на TypeScript, якія викорыстоўваюць толькі синтаксіс, які можна выдаліць, можна запускаць безпосередзя завдака вбудованага адключэння типаў:
node src/bench.ts
Якщо ваш код викорыстоўвае синтаксіс, які нельга проста выдаліць, запускайте яго заместа таго за дапамогай pnpm exec tsx src/bench.ts. Як бы вы не выбралі, вжывайце той самы інструмент для всіх троха ціклоў, і выводзіце значэння часу з высокай раздзялованасцю, а не толькі паводле console.time, каб можна было пораўняваць і аналізаваць цыфры.
Потым заменіце некоректны пакет дадзенаў і запускайце знова. Найцікавейным рэзультатам у тестах 4.5 є праўоцэснасць пад час обробкі некоректных дадзенаў. Большасць людзей тэстуе толькі правільны JSON, але публічные канцэнтры выдзеляюць вельмі больш часу на адхоўленне некоректных або шкодзічных запытанняў.
Функцыйны тэст выканаеся раней за тэст на швальнасць
Перад порэйванам часоў, пераканайцеся, што кожны парсер яшчэ падае тую інфармацыю, якая неабходна вашам UI. Спадчайшая пераконтаванасць — это парсаць некоректны пэйлоад і вывесіць шлях першай проблемы:
const a = Invoice.safeParse(bad);
console.log(a.success === false ? a.error.issues[0]?.path : "valid?");
Якщо скомпіляваны парсер яшчэ вяртае issues[0].path, форма, якая выделяе проблемны поле, можа на яго перейсці. Якщо ён выклекае адсутнасцю шляху або вяртае толькі булева значэнне, вы больш не можете перадаць тое ж самае паведамленне на рэвэлі поля ў useActionState або іншы механізм статаусу, який адображае памылкі формы.
Гэта ўсходжаючая прымірка. Булева значэнне адпавядае на запыткі "Чы гэта вообща квітанція?" але не можа паведаміць, якое поле ў спрабе. Используйце булевы шлях як фільтр, напрыклад, перед працоўнікам, який обрабоўвае рядкі CSV, і застаўце safeParse там, дзе чалавек павінен прачытаць і выправіць проблему, напрыклад, у форме, якая адрасуўвае чырвоную рамку навакола поля для введэння электронной пашты.
Што зменяе і шта не зменяе компіляцыя
z.compile() не мае нічынага з кампайлярам TypeScript. Канцэптуальна гэта змена заключаецца у тым, што Zod стварае болей спецыялізаваны парсер для конкрэтнага шыматка дадзейнаў, таму процес обрабоцкі выкаанаюць менш узагальненым, інтэрпретатыўным спосабам. z.infer<typeof Invoice> застаецца незменным; зменяецца толькі об’ект, які вы вызываеце пад час выканання.
Два об’екты, а не адны
Компільваны пасэр — это копія схемы ў момент яе компіляцыі. Методы схемы Zod, такія як .optional(), вяртаюць новыя схемы, таму якщо пазней вы створыце варіянт, напрыклад, зробіце email необавязковым, і продовжыце викорыстоўваць CompiledInvoice, компільваны пасэр все раве будзе адхіляць заявку без email. Цэя поведзенне ўсунена, а не баг: вы трыважыце два незалежныя об’екты. Перекомпілюйце кожны раз, калі змянюецца визначэнне схемы, і утримвайцеся ад коду, які выглядае так, нібы ён моць нарадзіць змяну схемы пасля ўжо яе компіляцыі.
Компілюйце раз, пад час загрузкі модуля
Компілюйце на рэвэлі модуля, ніколі ўнутры кантролера запытаў. Компіляцыя за кожны запыт платае самэ тую вартасць налаштавання, якую праглядаліся зменшыць, і гэта можа лёгка супакоўваць роботу медленней, чым простае safeParse.
Дзе важнае заўсёдыцтва памяці
Скорачэнне выкарыстоўвання памяці вынікла з таго, як экземпляры схэмы зберагаюць своія методы. Якщо ваша прыменэнцыя стварае сваія схэмы аднойчы пад час завантажэння модуля, што ўзята як рекамендаваны падход, то малая верага, што вы пабачыце значныя змены ў памяці процэса. Экономія становіць большое значэнне, калі існуе вельмі вяліка колькасць экземпляраў схэмы, таму не чакайце, што цячка 784 байта з’явіцца у вашым RSS.
Зберагаеце ў серверы
Кампіляванне — это оптымізацыя з баку сервера. Размешчэнне z.compile у кліентскай складовай, напрыклад, на сторонцы налашчэнняў, дадае дапамогу браузеру, які будзе парсаваць ў максымум калькі пакетаў дадзеных.
Чытанне рэзультатаў
На гэтай схэме рачунку, пасля 10 000 ітерацый пасля розгару на аднам ноутбуку, адна запускання дала такія показнікі.
Валідныя пакеты дадзеных:
safeParse: 312 мс- кампіляваны парс: 141 мс
Нэвялікі пакеты дадзеных:
safeParse: 498 мс- boolean validate: 39 мс
- compiled parse: 188 мс
Ні адны з іх не досягае 16 разоў, за выключэнням параметра boolean для нэвялікіх дадзеных, який зьніклае да гэтага показніка. Улучшэнняядзецца ў напрамку, які быў обявлены, проста ў меньшай меры для болей сложных схем. Які показнік ўражае — validate для нэвялікіх дадзеных, — гэты ж параметр нельга выкорыстоўваць самастойна пасля формы.
Іншыя параметры таксама заслуговуюць на аднае рэчыпі:
- Памылкі: у гэтым тэсте скомпіляваны
safeParseяшчэ фіксаваў памылку для некоректнага адреса электронной пошты, таму форма могла яго і далей выкорыстоўваць. - Форма API: рэзультат
z.compile— это аднае окремае значэнне, якое трэба расследжваць як незменнае, фіксаванае функцыя.
Меры гэтых цифраў
Гэтыя рынкі атрыманы на базе адной схемы, аднаго комп’ютера та 10 000 ітерацый ціклу пасля розгарячэння. Цифра 16 разоў аднасецца да пераверкі некоректных дадзеных у саўеўнічым тесте; гэты тест паказаў значныя паліпшэння, а не ту заголовочную цифру. Цифра памяці на адну строчку не была пераканальвана незалежна. А жорсткі цикл hrtime мерыць вартасць CPU, а не затрымку для корыстніка: гэта нічога не сказвае пра Web Vitals для рэальнага візітара на рэальной сетцы.
Вашы пакеты дадзеных буду разнымі. Якщо ваша схема ўскладнена адзіным элементам z.string(), ўжо сама компіляцыя не дапаможа паспешна адпрацаваць запит. Якщо ж мова пра вялікі вложаны об’ект на зайнятым шляху, трэба зафіксаваць час його обробкі. Адрабатайте 10 000 вярных і 10 000 невярных даных, а таксама зафіксавайце часы обробкі, а таксама шлях першай проблемы пад час обробкі невярных даных.
Сцэнарый абярання, які варта вывучыць
Разглянем ситуацыю імпорту CSV, дзе пад час циклу выклікаўся метод Invoice.parse. Вярныя рядкі обрабовваліся быстра, але невярныя рядкі стваралі вельмі складныя структуры дадзеных, чынічы працю програмы медленной. Змена способу пераверкі на булевы метод выправіла працэздатнась, але тая ж змена была застосавана і да шляху UI, які мяў паказваць значэння total. Рэзультат: значэнне "na" спрычыніла тыху адмову без пазначэння назвы поля, а бракаваючая вялічына потрапіла ў звіт через іншы шлях, які паверяўся булеваму значэнню.
Прагматыка, яка толькі пагародзіла ситуацыю, была складаннемя схемы ўнутры кэшера з кожным запитам; вартасць налагоджэння падрыхтавала абоўшчыну эфекта.
Што насправдзе працавала:
z.compileпад час завантажэння модуля, які выкарыстоўваецца працоўнікам імпорту;safeParseу форме, дзе чалавек можа прачытаць паказаннія пра адказы;- булевы параметр validate, які выступае толькі як фільтр пры роботе з очакальнай лістою запитаў.
Правілам ёсць такое: як толькі скомпільаваны парсер перестае фіксаваць issues[0].path, форма не можа яго выкарыстоўваць.
Запісуйте стан свайго сераўера прычым перад аналізам
Рэзультаты выконання маюць значэнне толькі ў сувязі з версіямі, якія іх стварылі. У прыкладным проекте — маленькам дапрыязе Next.js з чатырма маршрутамі, якія стосуюцца рахункоў-фактур, — былі выкарыстаны Node 24, TypeScript 7 і Next 16.3. Зберагуйте прыміткі з лабораторных экспэрыментаў у репазітарыі, напрыклад у файле notes/lab.md, каб кожна сесія запускалася на адных і тых жа падставах. Спачатку выведзіце версіі:
node -v
pnpm exec tsc -v
pnpm exec next --version
Запішыце іх у верхней часты прымітак. Якщо значныя версіі не збігаюцца з тымі, якія прыпускае інструкцыя, зупініцеся і узгодзіце ўсё, прычымляючыся да роботы, таму што пазнейшыя каманды можа даць правдападобныя, але вводзячыя ў галошу рэзультаты.
Далей запускіце сервер разработчика і праходзіце па маршрутам, каб пераканацца, што дапрыязе працуе нормальна, перш чым вы канаціце якія-небудзь вимеры:
pnpm exec next dev
Адзейце /, /invoices, /invoices/1, /settings і зноў /invoices, увімкнуўшы опцыю „Зберагчы лог“ у DevTools, і зафіксавайте стан палаты фільтраў разам з URL-адрэсайом.
Потым запустіце перакальвалювач типаў і выведзіце статус його завершэння:
pnpm exec tsc --noEmit --pretty false
echo $?
Код завершэння 0 не ўсунецца падтверджэнням таго, што прыклад програмы работае; гэта значыць толькі, што типы супадаюць, і гэта дае вам право перайсці да перакальвалювання пад час выконання. Пасля гэтага самі запустіце команды для теставання на выдатнасьць з раздзелу репродукцыі, а не пакладайцеся на чужыя цифры. Апаратная частка, тэмпература атмасферы, навантажэння на памяць і ўсе інша, што працуе на апараты, можа змяніць час выконання больш, ніж мінорны выпуск бібліятэкі.
Таксама цікава ідея — дадзіць у кожнай зметке адна-рэчовая лінію з описам «неудачнай спробы выправлення» у формате «спробавана X, але все равна бачыцца Y». Такія лініі зберагаюць правдзівую інформацыю і не дазволяюць два разы перапрацоўваць той самы безвыходны варіант.
Пашчэрпнутыя працы
- Кампіляванне парсера ў кліентскай складовай. Разметка стала большая, а інструменты пачалі скаржыцца; выправленняя заключалася ў перанесенні яго ў рабочы процэс.
- Перакананне на базе логічных значэнняў у форме. Ён вяртаў
falseбез парадку, заставляючы корыстніка сякліцца на безполезны, порожні стан аднойчыны; патрэбна была вярнуцьsafeParse. - Змена схемы пасля кампілявання. Кампільаванный парсер застаўся з старымі правіламі; кращэ перекампіляваць занова.
Спіс пераконтраць пры кампіляванні схемы
- Кампіляваць пад час завантажэння модуля, а не за кожны запит.
safeParse на формах і пераканайцеся, што яны ўсё такі чытають issues[0].path.Заставьце всі пяць цых часоў з вашага власнага тэставання. Адзін статыстыкі від праўодавца не дае жадных вястей пра вашу систему.
Дзе скомпіляванне даслужвае сваю практычнасць
У даплі з рахункамі-фактурамі імпортар CSV апрэчыўся ўдзельнай працою, якая давала рэзультаты. Форма налашчання ніколі яе не патрабавала, таксама як і webhook, який прыме ўсьмо толькі адзін рахунок-фактуру. Але ўсё ж такі процес компіляцыі вызваў іншую проблему: калі ў памяці было два об’екты схемы, разработчык вырашыў, што экспортаваная схема — „тая, якая была скомпілявана“, зменіў ёю, а працуючы парсер проігнораваў гэтыя змены. Гэтыя компіляцыі былі анульваны.
Політыка, якая застаецца, ў кашточку такая:
- скомпілюйце схемы, якія працуюць у циклах або на справжніх „гарячых“ траекторыях;
- не скомпілюйце схемы для аднаго толькі обробніка POST;
- выкорыстоўваеце булевую пераканальніцу як бар’ер у черге;
- выкорыстоўваеце
safeParse, калі людзі мусяць чытаць траекторыю абохвату проблемы.
Запісуйце гэтыя правілы як каментары ў верхней частцы файла схемы, там яны застануцца дыяўальнымі нават пасля выпуску будзь-яга блога чы размовнага тэпу:
// bench: pnpm exec tsx src/bench.ts
// expect: valid safeParse ~300ms / compiled ~140ms on 10k
// form: safeParse only
// worker loop: CompiledInvoice
// queue gate: Invoice.validate
Якщо заголовак і код не супяжнуюцца, адначасова які-небудзь з іх некоректны, таму трэба іх выправіць тыя ж дзень. Заголовак, які паводзіцца "form: safeParse only" над формай, яка на самай працэ выклікае функцыю validate, адносна тое, як і адбываецца сцэнарый неудачы, працавання якога было описана вышэй.
Ёсць больш дакледнай інфармацыі пра аднароджэнне схем межу кліентам і серверам у стацыянцы „Аднароджэнне адной схемы Zod межу фронтэндам на React і бэкэндам на Node“.
Ключовыя выводы
z.compile()заменяе адноразовую настройку на быстрэйшы парсер пад час выконання; гэта мае сенс толькі тады, калі схема выконвана часта, таму яе трэба скомпіляваць пад час завантажэння модуля на сервере.- Булевы шлях validate працюе значна быстрэй пад некоректным дадзеным, таму што ён прыпінае обработку дрэва проблем, і самэй гэтаму ён ніколі не можа выкарыстоўваться для формы, якой патрэбны падтрымкі на рэвэле полей.