Соглашения об именовании

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

Ключевые принципы:

  • единообразие стиля во всём проекте
  • совпадение имен схем и доменных сущностей
  • отсутствие избыточных префиксов и технического шума
  • согласованность между схемой и TypeScript-типом
  • предсказуемая семантика имен функций и переменных

Основное правило: имя должно отражать не реализацию, а смысл данных.


Именование схем

Схемы в Zod чаще всего представляют доменные объекты или DTO. Их именование обычно строится по следующим стратегиям:

Доменные сущности

При описании сущностей предметной области используется существительное в единственном числе:

const user = z.object({
  id: z.string(),
  email: z.string().email()
});

Имя user предпочтительнее вариантов userSchema, UserSchema, если контекст уже очевиден (например, файл user.ts).

Явные схемы с префиксом Schema

В проектах с большим количеством сущностей допустим явный префикс:

const userSchema = z.object({
  id: z.string(),
  email: z.string().email()
});

Такой подход используется, когда:

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

Однако избыточное повторение Schema ухудшает читаемость при масштабировании.

Схемы действий (DTO)

Для входных/выходных данных API используется глагольная или контекстная форма:

const createUserInput = z.object({
  email: z.string().email(),
  password: z.string().min(8)
});
const updateUserPayload = z.object({
  email: z.string().email().optional()
});

Рекомендуемые суффиксы:

  • Input
  • Output
  • Payload
  • Params
  • Query

Именование типов, выводимых из схем

Типы, получаемые через z.infer, должны иметь прямую связь со схемой, но не дублировать её техническое название.

type User = z.infer<typeof user>;

Если используется явный суффикс Schema:

const userSchema = z.object({...});
type User = z.infer<typeof userSchema>;

Важно избегать избыточных конструкций:

  • UserSchemaType — избыточно
  • TUser — допустимо, но устаревающий стиль
  • IUser — не рекомендуется в контексте TypeScript-экосистемы

Наиболее чистый вариант:

  • User для типа
  • user или userSchema для схемы

Именование полей объектов

Поля внутри схем должны следовать единому стилю API и не зависеть от внутренней реализации.

camelCase как основной стандарт

const product = z.object({
  productId: z.string(),
  createdAt: z.string(),
  isActive: z.boolean()
});

camelCase обеспечивает согласованность с TypeScript и JavaScript API.

snake_case при интеграции с внешними системами

При работе с legacy API или базами данных допускается сохранение исходного формата:

const product = z.object({
  product_id: z.string(),
  created_at: z.string()
});

Однако преобразование через transform часто предпочтительнее унификации.


Именование при трансформациях данных

Метод transform в Zod создаёт новую форму данных, поэтому результат должен иметь новое осмысленное имя.

const userDto = userSchema.transform((data) => ({
  id: data.id,
  emailAddress: data.email
}));

Рекомендуемые подходы:

  • исходное имя: user
  • трансформированное: userDto
  • для API: userResponse

Запрещено:

  • повторное использование исходного имени без уточнения
  • использование data как конечного публичного типа

Именование refine и superRefine правил

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

const password = z.string().refine(isStrongPassword, {
  message: "Weak password"
});

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

const isValidAge = (value: number) => value >= 18;

const user = z.object({
  age: z.number().refine(isValidAge)
});

Для superRefine предпочтительно выделение логики:

const validateUserBusinessRules = (data: User, ctx: z.RefinementCtx) => {
  if (data.age < 18) {
    ctx.addIssue({
      code: "custom",
      message: "User must be adult"
    });
  }
};

Принцип:

  • название отражает бизнес-логику
  • избегается технический стиль вроде check1, validateFn

Именование файлов со схемами

Файловая структура напрямую влияет на восприятие схем.

Один домен — один файл

user.ts
product.ts
order.ts

Разделение по слоям

user.schema.ts
user.types.ts
user.api.ts

Однако в экосистеме Zod чаще применяется объединённый файл:

user.ts

в котором находятся:

  • schema
  • inferred types
  • helper functions

Именование переиспользуемых схем

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

Базовые примитивы

const email = z.string().email();
const id = z.string().uuid();

Составные блоки

const timestampFields = z.object({
  createdAt: z.string(),
  updatedAt: z.string()
});

Общие ограничения

const paginationQuery = z.object({
  page: z.number().min(1),
  limit: z.number().max(100)
});

Недопустимо:

  • commonSchema
  • utilsSchema
  • helper1

Имена должны отражать назначение без абстрактного шума.


Соглашения для API-слоёв

В прикладных системах схемы часто разделяются по ролям:

  • Request: createUserRequest
  • Response: userResponse
  • Params: userParams
  • Query: userQuery

Пример:

const getUserParams = z.object({
  id: z.string()
});

const userResponse = z.object({
  id: z.string(),
  email: z.string()
});

Такое разделение снижает неоднозначность между входными и выходными структурами.


Именование ошибок и сообщений

Хотя сообщения ошибок не являются частью имени схемы, они формируют семантику валидации.

z.string({
  required_error: "email is required",
  invalid_type_error: "email must be a string"
});

Рекомендуется:

  • использовать нижний регистр для сообщений
  • избегать дублирования названия поля внутри текста
  • сохранять нейтральную формулировку

При кастомной логике:

message: "invalid user age range"

Согласованность между схемами и типами

На уровне архитектуры критично поддерживать симметрию:

  • userUser
  • orderOrder
  • createUserInputCreateUserInput

Несоответствия приводят к:

  • дублированию типов
  • разрыву между runtime и compile-time
  • усложнению рефакторинга

Избежание избыточной технической терминологии

Схемы в Zod не требуют:

  • T
  • I
  • SchemaType
  • DataModel

Чистые имена повышают читаемость:

  • user вместо IUserSchema
  • order вместо OrderSchemaType
  • createUserInput вместо TCreateUserInputSchema