Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Generic Union в TypeScript
Generic Union — это комбинация двух концепций TypeScript: generics (обобщённые типы) и union types (объединённые типы). Это позволяет создавать функции и типы, которые могут работать с несколькими вариантами типов, оставаясь типобезопасными.
Union Types (Объединённые типы)
Унион позволяет переменной или параметру иметь один из нескольких типов:
type Result = string | number | boolean;
const value1: Result = "hello"; // OK
const value2: Result = 42; // OK
const value3: Result = true; // OK
const value4: Result = null; // Ошибка!
Generics (Обобщённые типы)
Generic позволяет создавать типы и функции, которые работают с любым типом данных:
function getValue<T>(item: T): T {
return item;
}
const stringValue = getValue<string>("hello");
const numberValue = getValue<number>(42);
Что такое Generic Union
Generic Union — это использование union типов внутри generic типов. Это создаёт гибкость, позволяя функции или типу работать с несколькими вариантами, при этом сохраняя типобезопасность.
// Generic Union на уровне параметра
function process<T extends string | number>(value: T): T {
return value;
}
process("hello"); // OK
process(42); // OK
process(true); // Ошибка! boolean не входит в union
Практические примеры
Обработка разных типов данных
// Generic function с union constraint
function stringify<T extends string | number | boolean>(value: T): string {
return String(value);
}
stringify("hello"); // "hello"
stringify(42); // "42"
stringify(true); // "true"
Работа с результатом (Success или Error)
// Generic Result тип с union
type Result<T> =
| { status: "success"; data: T }
| { status: "error"; error: string };
function handleResult<T extends string | number>(result: Result<T>): void {
if (result.status === "success") {
console.log(result.data); // T
} else {
console.log(result.error);
}
}
const stringResult: Result<string> = { status: "success", data: "hello" };
const numberResult: Result<number> = { status: "success", data: 42 };
handleResult(stringResult); // OK
handleResult(numberResult); // OK
API Response с несколькими типами
// Различные типы ответов
type ApiResponse<T> =
| { status: 200; body: T }
| { status: 404; error: "Not Found" }
| { status: 500; error: "Server Error" };
function processResponse<T extends object | string | number>(
response: ApiResponse<T>
): void {
switch (response.status) {
case 200:
console.log("Success:", response.body);
break;
case 404:
console.log("Not found");
break;
case 500:
console.log("Server error");
break;
}
}
interface User {
id: number;
name: string;
}
const response: ApiResponse<User> = {
status: 200,
body: { id: 1, name: "Alice" }
};
processResponse(response); // OK
Union of Generics
Это другой вариант — когда сам generic может быть union:
// T может быть string ИЛИ number
function getValue<T extends string | number>(type: T): T {
return type;
}
type MyType = getValue<string | number>; // string | number
Сложные примеры
Conditional types с Generic Union
// Выбираем разные логики в зависимости от типа
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
// С union типом
type CheckType<T extends string | number> = T extends string
? "it's a string"
: "it's a number";
type Result1 = CheckType<string>; // "it's a string"
type Result2 = CheckType<number>; // "it's a number"
Mapped types с Generic Union
// Создание новых типов на основе union
type Getters<T extends string | number | boolean> = {
[K in T as `get${Capitalize<string & K>}`]: () => K;
};
type StringGetters = Getters<"name" | "email">;
// {
// getName: () => "name";
// getEmail: () => "email";
// }
Ограничение Generic Union
Можно ограничить, какие типы могут быть использованы с extends:
// Только примитивные типы
function handle<T extends string | number | boolean>(value: T): void {
console.log(value);
}
handle("hello"); // OK
handle(42); // OK
handle(true); // OK
handle({ x: 1 }); // Ошибка!
// Только объекты
function handleObject<T extends object>(
obj: T
): void {
console.log(obj);
}
handleObject({ name: "Alice" }); // OK
handleObject("string"); // Ошибка!
Реальные сценарии использования
Хук React с несколькими типами данных
function useData<T extends string | number>(
id: T
): { data: T; loading: boolean; error: Error | null } {
// Логика загрузки данных
return { data: id, loading: false, error: null };
}
const { data } = useData<string>("abc");
// data тип: string
Validation функция
function validate<T extends string | number | boolean>(
value: T
): { isValid: boolean; value: T } {
return { isValid: true, value };
}
const result = validate("hello");
// result.value тип: string
Итог
Generic Union в TypeScript — это мощный инструмент для создания типобезопасного кода, который может работать с несколькими вариантами типов. Он используется для создания гибких функций и компонентов при сохранении типобезопасности, ограничения возможных типов, улучшения читаемости кода и предотвращения ошибок типизации. Это особенно полезно при работе с API, формами и другими местами, где нужна гибкость при сохранении типобезопасности.