Цепочки трансформаций

Цепочки трансформаций в 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 обработки данных.


Разделение валидации и преобразования

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

  1. Сначала выполняется валидация типа и ограничений.
  2. Затем применяются преобразования.
  3. После этого возвращается итоговое значение.
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 и transform

preprocess работает до валидации, тогда как 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 в инструмент для построения данных с внешними зависимостями.


Порядок выполнения в цепочках

При построении сложных схем важно учитывать порядок операций:

  1. preprocess
  2. базовая валидация
  3. refine
  4. transform
  5. pipe (как связка схем)
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)

Эта модель позволяет строить сложные преобразования данных без выхода за пределы декларативного стиля и без необходимости ручного управления промежуточными состояниями.