Значения по умолчанию

Значения по умолчанию позволяют автоматически подставлять данные, если поле отсутствует или имеет значение undefined. В библиотеке Zod для этого используется метод .default().

Подход особенно полезен при:

  • обработке конфигураций;
  • работе с формами;
  • создании DTO;
  • нормализации API-ответов;
  • заполнении частично переданных объектов;
  • обработке переменных окружения.

Метод .default()

Базовый синтаксис:

import { z } from "zod";

const schema = z.string().default("guest");

Если входное значение отсутствует:

schema.parse(undefined);

Результат:

"guest"

Если значение передано:

schema.parse("admin");

Результат:

"admin"

Как работает .default()

Метод .default() срабатывает только в двух случаях:

  • значение отсутствует;
  • значение равно undefined.

Пример:

const schema = z.number().default(100);

schema.parse(undefined); // 100

Но:

schema.parse(null);

Ошибка:

ZodError

null не считается отсутствующим значением.


Значения по умолчанию для строк

const usernameSchema = z.string().default("anonymous");

usernameSchema.parse(undefined);

Результат:

"anonymous"

Комбинация с ограничениями:

const schema = z
  .string()
  .min(3)
  .max(20)
  .default("guest");

Важно понимать порядок обработки:

  1. сначала применяется default;
  2. затем выполняется валидация.

Если значение по умолчанию не проходит проверку:

const schema = z.string().min(10).default("abc");

Ошибка возникнет даже без входных данных:

schema.parse(undefined);

Значения по умолчанию для чисел

const schema = z.number().default(0);

schema.parse(undefined);

Результат:

0

С диапазоном:

const ageSchema = z
  .number()
  .min(0)
  .max(120)
  .default(18);

Значения по умолчанию для булевых значений

const schema = z.boolean().default(false);

Пример:

schema.parse(undefined); // false
schema.parse(true);      // true

Типичный случай — флаги конфигурации:

const configSchema = z.object({
  debug: z.boolean().default(false),
  cache: z.boolean().default(true),
});

Значения по умолчанию для массивов

const schema = z.array(z.string()).default([]);

Пример:

schema.parse(undefined);

Результат:

[]

Массив может содержать сложные структуры:

const tagsSchema = z
  .array(
    z.object({
      name: z.string(),
    })
  )
  .default([]);

Значения по умолчанию для объектов

const userSchema = z.object({
  name: z.string().default("Unknown"),
  age: z.number().default(18),
});

Пример:

userSchema.parse({});

Результат:

{
  name: "Unknown",
  age: 18
}

Частичное заполнение объекта

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

const settingsSchema = z.object({
  theme: z.string().default("light"),
  notifications: z.boolean().default(true),
  language: z.string().default("en"),
});

Входные данные:

{
  theme: "dark"
}

Результат:

{
  theme: "dark",
  notifications: true,
  language: "en"
}

.default() и .optional()

Часто возникает вопрос: нужен ли .optional() вместе с .default()?

Обычно — нет.

const schema = z.string().default("guest");

Такое поле уже допускает отсутствие значения.

Следующий код избыточен:

z.string().optional().default("guest");

Разница между .optional() и .default()

.optional()

Разрешает отсутствие поля.

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

schema.parse(undefined);

Результат:

undefined

.default()

Подставляет конкретное значение.

const schema = z.string().default("guest");

schema.parse(undefined);

Результат:

"guest"

Использование функций в .default()

Метод поддерживает функцию-генератор.

const schema = z.string().default(() => crypto.randomUUID());

Каждый вызов parse создаёт новое значение:

schema.parse(undefined);

Результат:

"f47ac10b-58cc..."

Генерация даты по умолчанию

const schema = z.date().default(() => new Date());

Пример:

schema.parse(undefined);

Результат:

2026-05-09T12:00:00.000Z

Генерация идентификаторов

const userSchema = z.object({
  id: z.string().default(() => crypto.randomUUID()),
  name: z.string(),
});

Значения по умолчанию и transform

default может использоваться вместе с трансформациями.

const schema = z
  .string()
  .default("42")
  .transform((value) => Number(value));

Результат:

schema.parse(undefined); // 42

Последовательность:

  1. применяется значение по умолчанию;
  2. выполняется transform.

Комбинация с preprocess

const schema = z.preprocess(
  (value) => {
    if (value === "") {
      return undefined;
    }

    return value;
  },
  z.string().default("empty")
);

Пример:

schema.parse("");

Результат:

"empty"

Подход полезен при обработке HTML-форм, где пустая строка часто означает отсутствие значения.


Значения по умолчанию в схемах конфигурации

Одна из самых распространённых областей применения.

const envSchema = z.object({
  PORT: z.coerce.number().default(3000),
  HOST: z.string().default("localhost"),
  DEBUG: z.coerce.boolean().default(false),
});

Использование:

const config = envSchema.parse(process.env);

Значения по умолчанию и coerce

coerce преобразует входные данные перед валидацией.

const schema = z.coerce.number().default(0);

Пример:

schema.parse("42");

Результат:

42

При отсутствии значения:

schema.parse(undefined);

Результат:

0

Значения по умолчанию в вложенных объектах

const schema = z.object({
  user: z.object({
    name: z.string().default("Anonymous"),
    role: z.string().default("user"),
  }),
});

Проблема:

schema.parse({});

Ошибка:

Required at "user"

Причина — объект user обязателен.

Правильный вариант:

const schema = z.object({
  user: z
    .object({
      name: z.string().default("Anonymous"),
      role: z.string().default("user"),
    })
    .default({}),
});

Теперь:

schema.parse({});

Результат:

{
  user: {
    name: "Anonymous",
    role: "user"
  }
}

Использование .catch() вместо .default()

Методы имеют разное назначение.

.default()

Срабатывает только при undefined.

.catch()

Срабатывает при ошибке валидации.

Пример:

const schema = z.number().catch(0);

schema.parse("abc");

Результат:

0

Сравнение:

z.number().default(0).parse(undefined); // 0
z.number().catch(0).parse("abc");       // 0

.nullish() и значения по умолчанию

.nullish() разрешает:

  • null;
  • undefined.
const schema = z.string().nullish();

Но default не заменяет null.

const schema = z.string().nullish().default("guest");

schema.parse(undefined); // "guest"
schema.parse(null);      // null

Принудительная замена null

Для обработки null используется preprocess.

const schema = z.preprocess(
  (value) => value ?? undefined,
  z.string().default("guest")
);

Теперь:

schema.parse(null);

Результат:

"guest"

Типизация значений по умолчанию

const schema = z.string().default("guest");

type Result = z.infer<typeof schema>;

Тип:

string

Несмотря на возможность отсутствия входного значения, результат после parse всегда содержит строку.


Поведение safeParse

const schema = z.string().default("guest");

const result = schema.safeParse(undefined);

Результат:

{
  success: true,
  data: "guest"
}

Ленивые вычисления через функцию

Функция в .default() вызывается только при необходимости.

const schema = z.string().default(() => {
  console.log("generated");
  return "value";
});

Пример:

schema.parse("test");

Функция не выполнится.

А здесь выполнится:

schema.parse(undefined);

Частые ошибки

Использование невалидного значения по умолчанию

z.string().email().default("abc");

Ошибка при parse(undefined).


Ожидание обработки null

z.string().default("x").parse(null);

null не заменяется автоматически.


Отсутствие .default({}) у вложенного объекта

const schema = z.object({
  settings: z.object({
    dark: z.boolean().default(false),
  }),
});

settings остаётся обязательным.


Практический пример: настройки приложения

const appConfigSchema = z.object({
  appName: z.string().default("My App"),

  server: z.object({
    host: z.string().default("localhost"),
    port: z.number().default(3000),
  }).default({}),

  features: z.object({
    auth: z.boolean().default(true),
    analytics: z.boolean().default(false),
  }).default({}),

  limits: z.object({
    maxUsers: z.number().default(1000),
    timeout: z.number().default(5000),
  }).default({}),
});

Входные данные:

{
  server: {
    port: 8080
  }
}

Результат:

{
  appName: "My App",

  server: {
    host: "localhost",
    port: 8080
  },

  features: {
    auth: true,
    analytics: false
  },

  limits: {
    maxUsers: 1000,
    timeout: 5000
  }
}

Практический пример: DTO пользователя

const createUserSchema = z.object({
  name: z.string(),

  role: z.enum([
    "user",
    "admin",
  ]).default("user"),

  isActive: z.boolean().default(true),

  createdAt: z.date().default(() => new Date()),
});

Использование:

createUserSchema.parse({
  name: "Alex"
});

Результат:

{
  name: "Alex",
  role: "user",
  isActive: true,
  createdAt: 2026-05-09T12:00:00.000Z
}