← Назад к вопросам

Что такое Generic Union?

2.3 Middle🔥 221 комментариев
#TypeScript

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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, формами и другими местами, где нужна гибкость при сохранении типобезопасности.