Цепочки трансформаций в Zod формируют последовательность преобразований значения после прохождения базовой проверки схемой. Они позволяют не только валидировать входные данные, но и приводить их к нужному виду, комбинировать несколько этапов обработки и строить декларативные конвейеры преобразований.
В Zod каждое значение проходит несколько стадий обработки. На уровне схемы можно выделить ключевую идею: входные данные не обязаны сразу соответствовать конечному типу, но должны быть приведены к нему через последовательность шагов.
Основной механизм трансформации реализуется через метод
transform, который изменяет результат после успешной
валидации.
import { z } from "zod";
const schema = z.string().transform((val) => val.length);
schema.parse("hello"); // 5
В данном случае строка сначала проходит проверку на тип
string, после чего преобразуется в число — длину
строки.
Трансформации в Zod являются композиционными: результат одной операции становится входом для следующей. Это позволяет строить цепочки преобразований, не нарушая декларативного характера схем.
const schema = z.string()
.transform((val) => val.trim())
.transform((val) => val.toUpperCase())
.transform((val) => val.split(""));
Результат выполнения:
schema.parse(" abc "); // ["A", "B", "C"]
Каждый transform выполняется последовательно, формируя
линейный pipeline обработки данных.
Важная особенность цепочек трансформаций заключается в разделении этапов проверки и изменения данных.
const schema = z.number().min(10).transform((n) => n * 2);
schema.parse(15); // 30
Если значение не проходит валидацию, трансформация не выполняется.
preprocess как начального звена цепочкиМетод preprocess позволяет внедрить преобразование до
этапа валидации. Это ключевой инструмент для нормализации входных
данных.
const schema = z.preprocess((val) => {
if (typeof val === "string") return parseInt(val, 10);
return val;
}, z.number());
Здесь входное значение приводится к числу до проверки схемой
z.number().
preprocess
и transformpreprocess работает до валидации, тогда как
transform — после неё.
Это различие определяет архитектурную роль каждого механизма:
preprocess отвечает за нормализацию входаtransform отвечает за преобразование уже валидных
данныхconst schema = z.preprocess((val) => Number(val), z.number())
.transform((n) => n * 10);
pipe как
механизм композиции схемМетод pipe позволяет связывать две схемы в
последовательный конвейер обработки.
const stringToNumber = z.string().transform((val) => parseInt(val, 10));
const schema = stringToNumber.pipe(z.number().min(100));
schema.parse("150"); // 150
Здесь результат первой схемы передаётся во вторую как входное значение.
pipe используется для построения многоуровневых систем
преобразования, где каждая схема отвечает за отдельный этап
обработки.
Трансформации часто приводят к изменению типа, что делает Zod одновременно валидатором и преобразователем.
const schema = z.string()
.transform((val) => val.split(","))
.transform((arr) => arr.map((x) => x.trim()))
.transform((arr) => arr.length);
schema.parse("a, b, c"); // 3
Тип данных последовательно изменяется:
string → string[] → string[] → number
Трансформации могут включать условную логику, не нарушая структуру схемы.
const schema = z.string().transform((val) => {
if (val.startsWith("#")) return val.slice(1);
return val;
});
Подобные конструкции часто используются для очистки или нормализации входных данных.
Zod поддерживает асинхронные трансформации, что позволяет включать в цепочку операции с внешними ресурсами.
const schema = z.string().transform(async (val) => {
const res = await fetch(`https://api.example.com/${val}`);
return res.json();
});
Такая схема требует использования parseAsync:
await schema.parseAsync("user1");
Асинхронные цепочки превращают Zod в инструмент для построения данных с внешними зависимостями.
При построении сложных схем важно учитывать порядок операций:
preprocessrefinetransformpipe (как связка схем)const schema = z.string()
.refine((val) => val.length > 2)
.transform((val) => val.toUpperCase());
Значение сначала проверяется, затем преобразуется.
refine на
цепочкиrefine добавляет дополнительную проверку, но не изменяет
значение. Он может разрывать цепочку, если условие не выполнено.
const schema = z.number()
.refine((n) => n % 2 === 0)
.transform((n) => n / 2);
Если число нечётное, дальнейшие преобразования не выполняются.
Цепочки трансформаций сохраняют детерминированность: одно и то же входное значение всегда проходит одинаковый путь преобразований.
Это позволяет рассматривать схемы как функции вида:
input → validation → transformation pipeline → output
Каждый этап является чистой функцией, что упрощает композицию и тестирование.
Трансформации могут включать вызов других схем, формируя вложенные цепочки обработки.
const numberSchema = z.number();
const schema = z.string().transform((val) => {
return numberSchema.parse(val);
});
Таким образом одна схема используется внутри другой, расширяя возможности композиции.
На практике цепочки часто включают нормализацию входных данных перед основной логикой.
const schema = z.preprocess((val) => {
if (typeof val === "string") return val.trim().toLowerCase();
return val;
}, z.string())
.transform((val) => val.replace(/-/g, "_"))
.transform((val) => val.split("_"));
Результатом становится структурированный массив токенов, полученный из произвольного текстового ввода.
При ошибке на любом этапе до transform выполнение
прекращается. Это означает, что трансформации не применяются к
невалидным данным.
const schema = z.number().min(10).transform((n) => n * 2);
schema.parse(5); // ошибка, transform не выполняется
Такое поведение обеспечивает предсказуемость и защищает от некорректных преобразований.
Цепочка трансформаций в Zod представляет собой последовательный конвейер, где данные проходят через:
preprocess)refine)transform)pipe)Эта модель позволяет строить сложные преобразования данных без выхода за пределы декларативного стиля и без необходимости ручного управления промежуточными состояниями.