При работе со сложными схемами в Zod основная трудность возникает не
на этапе описания типов, а при интерпретации причин провала валидации.
Чем глубже вложенность объектов, массивов, объединений и трансформаций,
тем менее очевидным становится источник ошибки. В таких условиях
ключевым становится системный подход к анализу ZodError,
структуры issues и поведения композиционных схем.
Любая ошибка валидации в Zod сводится к объекту
ZodError, содержащему массив issues. Каждый
элемент описывает конкретное нарушение:
path — путь до значения в структуре данныхmessage — текстовое описание проблемыcode — тип ошибки (например, invalid_type,
too_small, unrecognized_keys)При анализе сложных схем основная задача — не просто прочитать сообщение, а восстановить контекст, в котором оно возникло.
Вложенные объекты формируют массив путей, который может включать индексы массивов и ключи объектов одновременно:
path: ["user", "addresses", 2, "zipCode"]
Такой путь означает:
user — корневой объектaddresses — массив2 — третий элемент массиваzipCode — конкретное полеПри большом количестве ошибок критично группировать их по
path, а не рассматривать изолированно. Это позволяет
выявлять системные проблемы в структуре входных данных.
Использование safeParse вместо parse
позволяет избежать исключений и получить структурированный
результат:
const result = schema.safeParse(data);
if (!result.success) {
console.log(result.error.issues);
}
В сложных схемах это позволяет:
В глубоко вложенных схемах одна ошибка часто порождает десятки вторичных. Например, если объект не соответствует типу, все вложенные поля могут быть помечены как некорректные.
Типичный пример:
user не объектuser.name,
user.profile.ageТакие ситуации требуют фильтрации первопричин. Практически полезно
выделять ошибки с минимальным path как первичные.
Поле code позволяет классифицировать ошибки:
invalid_type — несоответствие типаtoo_small / too_big — нарушения
ограниченийinvalid_union — не совпал ни один вариантunrecognized_keys — лишние поляcustom — ошибки из refine или
superRefineПри отладке сложных схем важно группировать ошибки по
code, поскольку это часто быстрее приводит к источнику
проблемы, чем анализ сообщений.
z.union([...]) и особенно
z.discriminatedUnion часто становятся источником
непрозрачных ошибок.
В случае обычного union Zod пытается проверить каждую схему и возвращает агрегированный результат. Это приводит к ситуации, когда:
invalid_union скрывает реальную причинуСтруктура unionErrors внутри ZodError
позволяет разложить каждую попытку отдельно:
При анализе важно не рассматривать итоговую ошибку как единичную, а раскладывать её по альтернативам.
При использовании discriminatedUnion основной источник
проблем — некорректное значение дискриминирующего поля.
Типичная ситуация:
z.discriminatedUnion("type", [
z.object({ type: z.literal("a"), value: z.string() }),
z.object({ type: z.literal("b"), value: z.number() })
])
Если type отсутствует или имеет другое значение, Zod
сразу отбрасывает все варианты.
Отладка сводится к проверке:
literalsuperRefine позволяет добавлять кастомные проверки, но
усложняет диагностику, так как ошибки создаются вручную:
schema.superRefine((val, ctx) => {
if (val.a < val.b) {
ctx.addIssue({
code: "custom",
message: "a must be >= b",
path: ["a"]
});
}
});
Основные сложности:
codeПри отладке важно разделять:
transform может маскировать исходное значение, что
усложняет диагностику:
z.string().transform((val) => Number(val))
Если ошибка возникает после трансформации, исходное значение уже недоступно в привычной форме.
Для отладки используют:
TypeScript-типизация Zod через z.infer может создавать
иллюзию корректности схемы, даже если runtime-валидация ломается.
Типичные расхождения:
При анализе сложных схем важно проверять именно runtime-структуру, а не только типы.
Ошибки в массивах часто выглядят перегруженными:
path: ["items", 5, "price"]
При массовых ошибках полезно:
Часто оказывается, что ошибка не в массиве как структуре, а в единичной модели элемента.
Для сложных схем практическая отладка требует нормализации:
pathcodeЭто позволяет превратить массив issues в
структурированную карту проблем, где каждый узел соответствует
конкретной ветке данных.
В сложных композициях полезно разбивать схемы на этапы:
Каждый этап проверяется отдельно, что позволяет локализовать источник ошибки без анализа всей цепочки.
Некоторые ситуации создают иллюзию неправильной схемы:
undefined вместо отсутствующего поляnullNaN в числахВ таких случаях ошибка Zod корректна, но данные не соответствуют контракту.
Диагностика в Zod сводится к последовательному разложению:
issuespathcodeЧем сложнее схема, тем важнее переход от чтения отдельных сообщений к системному восстановлению цепочки преобразований данных и точек, в которых они перестают соответствовать ожидаемой структуре.