В Zod числовой тип строится вокруг базового примитива
z.number(), который описывает допустимые значения типа
number в JavaScript. В отличие от простого приведения типов
в рантайме, схема Zod формирует строгую валидацию входных данных,
различая корректные числа и значения, которые лишь внешне похожи на
числа.
Ключевая особенность JavaScript-чисел заключается в едином типе для целых и дробных значений. Zod сохраняет эту модель, но добавляет семантические ограничения через цепочку методов:
import { z } from "zod";
const schema = z.number();
Такое определение допускает любые значения типа number,
включая дробные числа, отрицательные значения, ноль, а также специальные
значения Infinity и -Infinity, если они не
ограничены дополнительными правилами.
Базовая схема z.number() редко используется без
уточнений. Наиболее распространённые ограничения связаны с диапазонами
значений:
const age = z.number().min(0).max(120);
Методы:
min(value) — минимально допустимое значениеmax(value) — максимально допустимое значениеpositive() — строго больше нуляnonnegative() — больше или равно нулюnegative() — меньше нуляnonpositive() — меньше или равно нулюЭти ограничения реализуются через последовательные проверки, каждая из которых добавляет условие в цепочку валидации.
JavaScript не различает целые и дробные числа на уровне типа, поэтому Zod реализует проверку целочисленности явно:
const schema = z.number().int();
Метод int() проверяет, что число не содержит дробной
части. При этом используется строгое сравнение с округлённым значением,
что позволяет отсеивать значения вроде 1.5,
3.14, -2.7.
Пример поведения:
10 — проходит валидацию10.0 — проходит валидацию (эквивалент целого
числа)10.1 — отклоняетсяДополнительная проверка часто комбинируется с диапазоном:
const page = z.number().int().min(1);
JavaScript допускает значения NaN, Infinity
и -Infinity, но их использование в прикладной валидации
обычно нежелательно.
Zod по умолчанию не пропускает NaN,
несмотря на то что он имеет тип number:
z.number().parse(NaN); // ошибка
Однако Infinity и -Infinity проходят
базовую проверку z.number(), если не ограничены:
z.number().parse(Infinity); // допустимо
Для полного контроля используется комбинация ограничений:
const finiteNumber = z.number().finite();
Метод finite() исключает все специальные значения и
допускает только конечные числа.
Числа с плавающей точкой в JavaScript подчиняются стандарту IEEE 754, что приводит к известным проблемам точности:
0.1 + 0.2 !== 0.3
Zod не изменяет это поведение, но позволяет фиксировать допустимые
диапазоны и шаги значений через multipleOf:
const price = z.number().multipleOf(0.01);
Метод multipleOf проверяет кратность числа заданному
шагу. Это полезно для финансовых данных, где требуется ограничение до
копеек или центов.
В реальных входных данных числа часто приходят в виде строк. Для этого используется механизм приведения типов:
const schema = z.coerce.number();
coerce.number() выполняет автоматическое
преобразование:
"123" → 123"3.14" → 3.14"" → ошибка"abc" → ошибкаЭтот механизм применяется до основной валидации, что позволяет объединить парсинг и проверку в одну схему.
Схемы чисел часто строятся как комбинация нескольких правил:
const schema = z
.coerce.number()
.int()
.min(1)
.max(100);
Такое описание задаёт полный контракт:
Порядок методов имеет значение с точки зрения логики проверки, но не влияет на конечный результат интерпретации значения.
Когда встроенных ограничений недостаточно, используется
refine:
const schema = z.number().refine((val) => val % 2 === 0);
refine позволяет задавать произвольную логику проверки,
сохраняя при этом типизацию number. Для более сложных
сценариев можно использовать superRefine, позволяющий
добавлять детализированные ошибки:
const schema = z.number().superRefine((val, ctx) => {
if (val < 0) {
ctx.addIssue({
code: "custom",
message: "Число должно быть неотрицательным",
});
}
});
Zod предоставляет два основных режима обработки:
parse — выбрасывает исключение при ошибкеsafeParse — возвращает структурированный результатconst result = z.number().safeParse("123");
Результат содержит:
success: true и data, если значение
корректноsuccess: false и error, если проверка не
прошлаЭто позволяет использовать числовые схемы как в строгих API, так и в сценариях пользовательского ввода.
Каждая числовая схема в Zod автоматически выводит тип:
const schema = z.number().int();
type T = z.infer<typeof schema>; // number
Однако логические ограничения (например, int, диапазоны,
finite) не отражаются в TypeScript-типе, оставаясь
исключительно на уровне runtime-валидации. Это создаёт разделение между
структурным типом и фактическими ограничениями данных.
На практике числовые схемы часто формируют предсказуемые паттерны:
z.number().int().positive();
z.number().min(0).max(100);
z.number().min(-180).max(180);
z.number().finite().nonnegative().multipleOf(0.01);
Каждая комбинация формирует отдельную семантическую модель данных,
несмотря на общий примитив number.