Автоматический вывод типов в Yup позволяет синхронизировать схемы валидации с TypeScript-типами без ручного дублирования интерфейсов. Это снижает риск расхождений между описанием данных и их реальной структурой, а также упрощает поддержку кода в проектах, где валидация и типизация тесно связаны.
В основе автоматического вывода лежит утилитный тип
yup.InferType, который извлекает TypeScript-тип из
схемы.
import * as yup from "yup";
const schema = yup.object({
name: yup.string().required(),
age: yup.number().min(0).required(),
});
type User = yup.InferType<typeof schema>;
В результате User автоматически становится:
type User = {
name: string;
age: number;
};
Ключевая особенность заключается в том, что тип формируется не вручную, а на основе структуры схемы. Это особенно важно в крупных проектах, где количество полей может исчисляться десятками или сотнями.
Объектные схемы являются основным источником сложных структур. Yup рекурсивно анализирует каждое поле и собирает итоговый тип.
const schema = yup.object({
id: yup.string().required(),
profile: yup.object({
firstName: yup.string(),
lastName: yup.string(),
}),
});
type Result = yup.InferType<typeof schema>;
Результирующий тип:
type Result = {
id: string;
profile?: {
firstName?: string;
lastName?: string;
};
};
Особенность заключается в том, что optional() и
required() напрямую влияют на итоговую обязательность полей
в TypeScript.
При использовании массивов вывод типов распространяется на каждый элемент коллекции.
const schema = yup.object({
tags: yup.array().of(yup.string().required()),
});
Тип будет интерпретирован как:
type Result = {
tags?: string[];
};
Если массив содержит объекты, типизация становится вложенной:
const schema = yup.object({
items: yup.array().of(
yup.object({
id: yup.number(),
title: yup.string(),
})
),
});
Результат:
type Result = {
items?: {
id?: number;
title?: string;
}[];
};
Yup поддерживает преобразование значений, что может менять итоговый тип данных. Однако TypeScript не всегда способен автоматически отследить такие изменения.
const schema = yup.object({
count: yup.number().transform((value, originalValue) => {
return typeof originalValue === "string"
? parseInt(originalValue, 10)
: value;
}),
});
Несмотря на трансформацию, тип остаётся number, так как
система вывода ориентируется на базовый тип схемы, а не на
runtime-логику.
Различие между nullable() и optional()
отражается в типах по-разному.
const schema = yup.object({
title: yup.string().nullable(),
description: yup.string().optional(),
});
Результат:
type Result = {
title: string | null;
description?: string;
};
Таким образом:
nullable() добавляет null в типoptional() делает поле необязательнымТип mixed() используется для значений, которые не имеют
строгого ограничения. В этом случае вывод становится максимально
широким.
const schema = yup.object({
value: yup.mixed(),
});
Тип:
type Result = {
value?: any;
};
При отсутствии уточнений TypeScript теряет возможность строгой проверки.
При использовании oneOf Yup может выводить объединения
литеральных типов.
const schema = yup.object({
role: yup.string().oneOf(["admin", "user", "guest"]),
});
Тип:
type Result = {
role?: "admin" | "user" | "guest";
};
Это позволяет ограничивать допустимые значения без дополнительных интерфейсов.
При использовании when и условной логики вывод типов
становится менее точным, так как структура может зависеть от
runtime-значений.
const schema = yup.object({
type: yup.string(),
value: yup.mixed().when("type", {
is: "number",
then: yup.number(),
otherwise: yup.string(),
}),
});
В подобных случаях итоговый тип может расширяться до объединения:
type Result = {
type?: string;
value?: string | number;
};
TypeScript не способен полностью восстановить условную логику схемы, поэтому результат становится обобщённым.
as const и статической типизациейХотя Yup не требует использования as const, он может
усиливать точность вывода при сочетании с литеральными значениями.
const roles = ["admin", "user"] as const;
const schema = yup.object({
role: yup.mixed<typeof roles[number]>().oneOf(roles),
});
В этом случае тип остаётся строго ограниченным значениями массива.
Несмотря на высокую степень интеграции с TypeScript, механизм вывода имеет ограничения:
yup.lazy) не всегда корректно
типизируютсяmixed() приводит к ослаблению строгой типизацииconst schema = yup.lazy((value) => {
if (typeof value === "string") {
return yup.string();
}
return yup.number();
});
В таких случаях итоговый тип часто становится объединением или
unknown, в зависимости от конфигурации.
Использование yup.InferType позволяет строить
архитектуру, в которой схема становится единым источником истины для
структуры данных. Это устраняет необходимость поддерживать
параллельно:
Схема выступает центральным элементом, а типы автоматически выводятся из неё, снижая вероятность несоответствий между слоями приложения.