При разработке часто возникает необходимость в валидации каких-либо данных. Например, это могут быть внешние данные от клиента (в случае, если мы работаем над API), или сообщения из RabbitMQ.
И тут возникает проблема. Что делать, если мы хотим
- Валидировать без описания горы различных if, instanceof, typeof и прочего.
- Иметь TypeScript типы для представления нужных нам данных.
- Чтобы нам не приходилось одновременно поддерживать в актуальном состоянии и типы и код для валидации.
Для решения подобной задачи, можем воспользоваться библиотекой zod. Работу с ней мы и рассмотрим в этой статье.
Установка
Перед началом работы установим библиотеку:
$ npm install zod
# or via yarn
$ yarn add zod
Начало работы
Давайте начнём с небольшого практического примера.
Допустим, у нас есть тип “Пользователь” со следующими полями:
id
типаnumber
;email
типаstring
;name
типаstring
;- И
githubId
типаstring
(только для пользователей, которые привязали GitHub аккаунт, поэтому необязательное).
И мы хотим проверить, подходит ли переданный нам объект под этом тип.
Для начала, нам необходимо описать этот тип с помощью средств zod:
/**
* Import zod library
*/
import { z } from 'zod';
/**
* Define our User model using zod
*/
const User = z.object({
id: z.string(),
email: z.string(),
name: z.string(),
githubId: z.string().optional(),
});
Теперь попробуем проверить, соответствует ли объект этой схеме:
const invalidUser = {
id: 9,
email: '[email protected]',
}
const parsedUser = User.parse(invalidUser)
После запуска такого кода, мы получим ошибку, так как в объекте пользователя не хватает не хватает поля name:
Аналогично, zod скажет об ошибке, если поле неправильного типа данных:
const invalidUser = {
id: '9',
email: '[email protected]',
}
const parsedUser = User.parse(invalidUser)
Окей, а что с генерацией типов для TypeScript? Zod может предоставить нам их:
Теперь этот тип мы можем использовать в нашем коде:
Таким образом, описывая типы с помощью zod, мы получаем возможность легко валидировать данные в рантайме, а также использовать типы, чтобы узнавать об ошибках во время компиляции.
Мы рассмотрели один из самых простых кейсов использования zod, а сейчас рассмотрим что ещё может нам предложить эта библиотека.
Расширенная валидация строк и чисел
С помощью zod мы можем задать правила для длины, формата (url, email, uuid), минимальных и максимальных значений и другие свойства:
/**
* Define our User model using zod
*/
const User = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().nonempty(),
githubId: z.string().optional(),
});
Валидация массивов
Пусть у каждого пользователя будет массив проектов. Опишем это:
const Project = z.object({
id: z.string().uuid(),
name: z.string()
});
const User = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().nonempty(),
githubId: z.string().optional(),
projects: z.array(Project)
});
Проверим работу:
const invalidUser = {
id: '694a8b3a-d782-44fe-ad35-4a63860ab742',
name: 'John',
email: '[email protected]',
projects: [{name: 'Zod'}]
}
Видим ошибку, всё работает верно:
Типы тоже на месте:
Работа с объектами
Zod предоставляет много различных методов для работы с объектами: наследование, объединение, исключение полей (omit), выборка полей (pick) и другие.
const UserWithoutId = User.omit({id: true})
const PartialUser = User.partial(); // all fields are optional
const WithImage = z.object({image: z.string()});
const UserWithImage = User.merge(WithImage);
Конвертация TypeScript определений в схемы zod
С помощью утилиты ts-to-zod мы можем делать наоборот — генерировать zod-схему из определений типов в TypeScript.
Давайте преобразуем с помощью этой утилиты TypeScript интерфейс в схему zod:
/**
* Workspace representation in DataBase
*/
export interface Workspace {
/**
* Workspace's id
*/
_id: string;
/**
* Workspace's name
*/
name: string;
/**
* Workspace's description
*/
description?: string;
/**
* Workspace's image URL
*/
image?: string;
/**
* Date when workspace was charged last time
*/
lastChargeDate: Date;
}
Запускаем команду для генерации схемы:
И смотрим результат:
// Generated by ts-to-zod
import { z } from "zod";
export const workspaceSchema = z.object({
_id: z.string(),
name: z.string(),
description: z.string().optional(),
image: z.string().optional(),
lastChargeDate: z.date(),
});
Теперь можно пользоваться полученной схемой для проверки данных.
По ссылке можно ознакомиться и с другими полезными утилитами экосистемы zod.
Заключение
На этом мы закончим краткий обзор библиотеки zod. Мы рассмотрели как эта библиотека помогает с валидацией данных и генерацией типов, чтобы вы были уверены в своих типах не только во время компиляции, но и во время выполнения кода.
С полным списком её возможностей можете ознакомиться в официальной документации.