Привет! Спасибо за разбор. В вашем примере с 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 и т.п.).
Ещё раз спасибо — поправлю формулировку в статье, чтобы не вводить в заблуждение. 👍
Привет! Спасибо за разбор. В вашем примере с 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 для конкретной ветки:И да, цель статьи не “похоронить Yup”, а показать наш практический опыт использования Zod как единый источник правды (особенно в связке с RHF, форматами ошибок, strict/strip/passthrough и т.п.).
Ещё раз спасибо — поправлю формулировку в статье, чтобы не вводить в заблуждение. 👍