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

Приведи пример использования Generics

2.0 Middle🔥 191 комментариев
#TypeScript

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

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

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

Generics в TypeScript

Generics (обобщённые типы) — это механизм TypeScript для создания компонентов, функций и классов, которые работают с разными типами данных, сохраняя при этом типобезопасность. Это один из ключевых инструментов для написания переиспользуемого и гибкого кода.

Базовый пример: Generic функция

Представь функцию, которая возвращает переданный аргумент как есть:

function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);      // num: number
const str = identity<string>("hello"); // str: string
const bool = identity(true);            // bool: boolean (тип выводится автоматически)

Здесь <T> — это переменная типа (type variable). Когда мы вызываем функцию, TypeScript автоматически определяет, какой тип подставить вместо T.

Generic компонент React

В React Generics позволяют создавать переиспользуемые компоненты со строгой типизацией:

interface CardProps<T> {
  data: T;
  render: (item: T) => React.ReactNode;
}

function Card<T>({ data, render }: CardProps<T>) {
  return (
    <div className="card">
      {render(data)}
    </div>
  );
}

// Использование
const userCard = (
  <Card<User>
    data={{ id: 1, name: "Alice", email: "alice@example.com" }}
    render={(user) => <p>{user.name} ({user.email})</p>}
  />
);

Generic с ограничениями (Constraints)

Можно ограничить, какие типы можно подставлять. Например, функция, которая работает только с объектами, имеющими свойство length:

interface Lengthwise {
  length: number;
}

function getLength<T extends Lengthwise>(arg: T): number {
  return arg.length;
}

getLength([1, 2, 3]);      // OK: array has length
getLength("hello");        // OK: string has length
getLength({ length: 10 }); // OK: object has length property
getLength(42);             // ERROR: number has no length

Generic хук React

Кастомные хуки часто используют Generics для работы с разными типами состояния:

function useFetch<T>(url: string): { data: T | null; loading: boolean; error: Error | null } {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((json: T) => {
        setData(json);
        setLoading(false);
      })
      .catch((err) => {
        setError(err);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
}

// Использование
interface Post {
  id: number;
  title: string;
  body: string;
}

const { data: posts, loading } = useFetch<Post[]>("/api/posts");

Multiple Type Parameters

Функция может иметь несколько переменных типов:

function swap<T, U>(a: T, b: U): [U, T] {
  return [b, a];
}

const result = swap("hello", 42); // result: [number, string]

Generic утилиты TypeScript

TypeScript имеет встроенные Generic типы для трансформации:

// Pick — выбрать только некоторые свойства
type UserPreview = Pick<User, "id" | "name">;

// Omit — исключить свойства
type UserWithoutPassword = Omit<User, "password">;

// Partial — все свойства опциональны
type PartialUser = Partial<User>;

// Record — создать объект с определённым набором ключей
type Roles = Record<"admin" | "user" | "guest", boolean>;
const roles: Roles = { admin: true, user: false, guest: false };

Когда использовать Generics

Используй Generics когда:

  • Функция/компонент работает с разными типами данных
  • Нужна типобезопасность без дублирования кода
  • Создаёшь переиспользуемые хуки или компоненты
  • Работаешь с асинхронным кодом (Promise, async/await)

Избегай Generics когда:

  • Работаешь с одним конкретным типом
  • Логика специфична для одного типа данных
  • Generics усложняют код без пользы

Практический пример: API response wrapper

interface ApiResponse<T> {
  success: boolean;
  data: T | null;
  error: string | null;
}

async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
  try {
    const response = await fetch(url);
    const data = await response.json() as T;
    return { success: true, data, error: null };
  } catch (err) {
    return {
      success: false,
      data: null,
      error: err instanceof Error ? err.message : "Unknown error",
    };
  }
}

// Использование
const result = await fetchData<{ id: number; name: string }>("/api/user");
if (result.success && result.data) {
  console.log(result.data.name);
}

Generics — это основа для написания чистого, безопасного и переиспользуемого кода в TypeScript. Они позволяют сохранить гибкость JavaScript, добавив при этом строгую типизацию.