Обновить
0
18
Сергей Русков@sruskov

Пользователь

Отправить сообщение

Привет! Спасибо за разбор. В вашем примере с react-hook-form вы правы: если передать useForm<TFormData> и использовать yupResolver(schema), TypeScript действительно поймает рассинхрон между типом и схемой (как у вас на скрине после добавления info).

Моя формулировка “рассинхронизация гарантирована” — слишком категоричная, поправлю: корректнее “если типы и схема живут отдельно, есть риск рассинхронизации; в RHF при правильной типизации часть таких ошибок ловится компилятором”.

При этом тезис статьи был про уменьшение дублирования в целом по проекту (не только формы как в вашем примере): валидация входящих данных/ответов API/локалстора и т.д. Там, если тип и схема разнесены, рассинхрон действительно легко получить.

И да, Yup умеет InferType, поэтому в типичных кейсах можно не описывать TFormData руками как в вашем примере, а выводить его из схемы — это как раз убирает дублирование.

Привет! Спасибо за комментарий — вы правы: Yup действительно умеет выводить тип из схемы (InferType / Asserts). В статье в этом месте я сформулировал некорректно — это моя ошибка (сказался старый опыт и недостаточно свежий ресёрч по Yup).

При этом тезис “Yup мало чем отличается от Zod” справедлив на базовом уровне, но на более сложных сценариях всплывают различия, в которых Zod показывает себя лучше.

Например, дискриминированные объединения (когда структура зависит от поля type). В рантайме оба валидатора справятся, но в TypeScript Zod обычно даёт более точное “сужение типов”, а в Yup тип часто остаётся размытым (условно string | undefined), даже если в рантайме поле required для конкретной ветки:

// Zod: сужение типов работает “как ожидаешь”
const ZCompany = z.object({ type: z.literal("company"), inn: z.string() });
const ZPerson = z.object({ type: z.literal("person"), ssn: z.string() });
const ZU = z.discriminatedUnion("type", [ZPerson, ZCompany]);
type ZT = z.infer<typeof ZU>;

function zodOk(x: ZT) {
  if (x.type === "company") x.inn.toUpperCase(); // ok
}

// Yup: рантайм ок, но TS часто не видит “если company — то inn точно есть”
const YU = yup.object({
  type: yup.mixed<"person" | "company">().oneOf(["person", "company"]).required(),
  inn: yup.string().when("type", { is: "company", then: (s) => s.required(), otherwise: (s) => s.strip() }),
  ssn: yup.string().when("type", { is: "person", then: (s) => s.required(), otherwise: (s) => s.strip() }),
});
type YT = yup.InferType<typeof YU>;

function yupWeaker(x: YT) {
  if (x.type === "company") x.inn.toUpperCase(); // TS: возможно undefined
}

И да, цель статьи не “похоронить Yup”, а показать наш практический опыт использования Zod как единый источник правды (особенно в связке с RHF, форматами ошибок, strict/strip/passthrough и т.п.).

Ещё раз спасибо — поправлю формулировку в статье, чтобы не вводить в заблуждение. 👍

Информация

В рейтинге
409-й
Работает в
Зарегистрирован
Активность

Специализация

Фронтенд разработчик
Ведущий
Git
Docker
JavaScript
TypeScript
React