В типизированных схемах основная цель именования заключается в том, чтобы структура данных оставалась предсказуемой, читаемой и однозначно интерпретируемой как в рантайме, так и на уровне TypeScript-типов. В Zod схемы являются одновременно валидаторами и источником вывода типов, поэтому выбор имен напрямую влияет на поддерживаемость кода.
Ключевые принципы:
Основное правило: имя должно отражать не реализацию, а смысл данных.
Схемы в Zod чаще всего представляют доменные объекты или DTO. Их именование обычно строится по следующим стратегиям:
При описании сущностей предметной области используется существительное в единственном числе:
const user = z.object({
id: z.string(),
email: z.string().email()
});
Имя user предпочтительнее вариантов
userSchema, UserSchema, если контекст уже
очевиден (например, файл user.ts).
В проектах с большим количеством сущностей допустим явный префикс:
const userSchema = z.object({
id: z.string(),
email: z.string().email()
});
Такой подход используется, когда:
Однако избыточное повторение Schema ухудшает читаемость
при масштабировании.
Для входных/выходных данных API используется глагольная или контекстная форма:
const createUserInput = z.object({
email: z.string().email(),
password: z.string().min(8)
});
const updateUserPayload = z.object({
email: z.string().email().optional()
});
Рекомендуемые суффиксы:
Типы, получаемые через 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 и не зависеть от внутренней реализации.
const product = z.object({
productId: z.string(),
createdAt: z.string(),
isActive: z.boolean()
});
camelCase обеспечивает согласованность с TypeScript и JavaScript API.
При работе с 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
}));
Рекомендуемые подходы:
useruserDtouserResponseЗапрещено:
data как конечного публичного типаПравила валидации часто остаются безымянными, но при росте проекта требуется их идентификация.
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
в котором находятся:
Переиспользуемые схемы должны иметь абстрактные и универсальные имена.
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)
});
Недопустимо:
commonSchemautilsSchemahelper1Имена должны отражать назначение без абстрактного шума.
В прикладных системах схемы часто разделяются по ролям:
createUserRequestuserResponseuserParamsuserQueryПример:
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"
На уровне архитектуры критично поддерживать симметрию:
user → Userorder → OrdercreateUserInput → CreateUserInputНесоответствия приводят к:
Схемы в Zod не требуют:
TISchemaTypeDataModelЧистые имена повышают читаемость:
user вместо IUserSchemaorder вместо OrderSchemaTypecreateUserInput вместо
TCreateUserInputSchema