Массивы и кортежи

Массивы в Zod описываются через z.array(), где в качестве аргумента передаётся схема элемента. Это базовый строительный блок для валидации списков однотипных значений.

import { z } from "zod";

const schema = z.array(z.string());

Такое определение означает, что на вход ожидается массив строк. Любое отклонение от этого типа приведёт к ошибке валидации.

schema.parse(["a", "b", "c"]); // корректно
schema.parse([1, 2, 3]);       // ошибка
schema.parse("a, b, c");       // ошибка

Ограничения массивов

Zod предоставляет набор методов для задания ограничений длины массива:

  • min(n) — минимальная длина
  • max(n) — максимальная длина
  • length(n) — точная длина
  • nonempty() — массив не может быть пустым
const schema = z.array(z.number()).min(2).max(5);

schema.parse([1, 2]);       // ok
schema.parse([1]);          // ошибка
schema.parse([1, 2, 3, 4]); // ok
const exact = z.array(z.string()).length(3);

exact.parse(["a", "b", "c"]); // ok
exact.parse(["a", "b"]);      // ошибка
const nonEmpty = z.array(z.boolean()).nonempty();

nonEmpty.parse([true]); // ok
nonEmpty.parse([]);     // ошибка

Вложенные массивы

Схемы могут быть произвольно вложенными, включая многомерные массивы:

const matrix = z.array(z.array(z.number()));

matrix.parse([
  [1, 2],
  [3, 4]
]); // корректно

Каждый уровень вложенности проверяется независимо, что позволяет точно контролировать структуру данных.

Преобразования массивов

Zod поддерживает преобразования значений через transform, что позволяет модифицировать массив после валидации.

const schema = z.array(z.string()).transform((arr) => arr.map(s => s.toUpperCase()));

schema.parse(["a", "b"]); // ["A", "B"]

Кортежи в Zod

Кортежи отличаются от массивов строгой фиксированной структурой. В отличие от z.array, где все элементы одного типа, z.tuple позволяет задавать разные типы для каждой позиции.

const tuple = z.tuple([z.string(), z.number(), z.boolean()]);

Такое описание означает:

  • первый элемент — строка
  • второй — число
  • третий — булево значение
tuple.parse(["id", 10, true]);  // корректно
tuple.parse(["id", "10", true]); // ошибка

Фиксированная структура кортежей

Кортежи строго фиксируют длину. Лишние или недостающие элементы считаются ошибкой.

const user = z.tuple([z.string(), z.number()]);

user.parse(["Alice", 25]);       // ok
user.parse(["Alice"]);           // ошибка
user.parse(["Alice", 25, true]); // ошибка

Rest-элементы в кортежах

Кортежи могут включать остаточный тип через .rest(). Это позволяет комбинировать фиксированную часть и произвольное количество элементов одного типа.

const schema = z.tuple([
  z.string(),
  z.number()
]).rest(z.boolean());

Теперь:

  • первый элемент — строка
  • второй — число
  • остальные — булевы значения
schema.parse(["id", 1, true, false, true]); // корректно
schema.parse(["id", 1]);                     // корректно
schema.parse(["id", 1, "no"]);              // ошибка

Опциональные элементы в кортежах

Элементы кортежа могут быть необязательными через z.optional():

const schema = z.tuple([
  z.string(),
  z.number().optional()
]);
schema.parse(["id", 10]); // ok
schema.parse(["id"]);     // ok

Различие массивов и кортежей

Массивы и кортежи решают разные задачи:

  • z.array — однородные коллекции
  • z.tuple — фиксированные структуры с разными типами

Сравнение:

const arr = z.array(z.string());
// ["a", "b", "c"]

const tup = z.tuple([z.string(), z.number()]);
// ["a", 1]

Массивы применяются для списков, где длина и структура не фиксированы. Кортежи — для строго заданных наборов значений, например координат, пар ключ-значение, конфигурационных параметров.

Вывод типов TypeScript

Zod автоматически выводит типы для массивов и кортежей.

const arr = z.array(z.string());
type A = z.infer<typeof arr>;
// string[]
const tup = z.tuple([z.string(), z.number()]);
type T = z.infer<typeof tup>;
// [string, number]

Для кортежей TypeScript сохраняет фиксированную позиционную типизацию, что делает их полезными в строго типизированных API.

Глубокие структуры с массивами и кортежами

Массивы и кортежи могут комбинироваться:

const schema = z.array(
  z.tuple([z.string(), z.number()])
);
schema.parse([
  ["a", 1],
  ["b", 2]
]);

Такие структуры часто используются при работе с сериализованными данными, таблицами и результатами API.

Практические особенности использования

При проектировании схем важно учитывать различие между гибкостью и строгостью:

  • массивы допускают вариативность длины
  • кортежи фиксируют контракт данных

Это влияет на архитектуру данных, особенно при взаимодействии с внешними API, где несоответствие структуры может приводить к ошибкам на уровне бизнес-логики, а не только валидации.

Комбинирование z.array, z.tuple, rest, min, max и вложенных схем позволяет описывать практически любые структурированные данные без выхода за рамки декларативной модели Zod.