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

Как ограничить Generic на входящие данные?

2.0 Middle🔥 183 комментариев
#Soft Skills и рабочие процессы

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

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

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

Как ограничить Generic на входящие данные

Generic constraints — это способ ограничить типы, которые можно подставить в Generic параметр. Вместо того чтобы функция принимала любой тип, ты можешь требовать, чтобы тип удовлетворял определённым условиям используя ключевое слово extends.

Базовое ограничение с extends

// ❌ Без ограничения - может быть любой тип
function getProperty<T>(obj: T, key: string): any {
  return obj[key]; // TypeScript ошибка: не знает есть ли key
}

// ✅ С ограничением - T должен быть объект
function getProperty<T extends object>(obj: T, key: keyof T): any {
  return obj[key];
}

const user = { name: "Alice", age: 30 };
getProperty(user, "name"); // ✅ OK
getProperty(user, "email"); // ❌ Ошибка: нет такого свойства

Ограничение на интерфейс

// Только объекты с полем id
interface HasId {
  id: string | number;
}

function findById<T extends HasId>(items: T[], id: string | number): T | undefined {
  return items.find(item => item.id === id);
}

// ✅ User имеет id
const users = [{ id: "1", name: "Alice" }];
findById(users, "1"); // OK

// ❌ Product не имеет id
const products = [{ sku: "ABC" }];
findById(products, "ABC"); // Ошибка

Ограничение на примитивные типы

// T должен быть только string или number
function processValue<T extends string | number>(value: T): T {
  return value;
}

processValue("hello"); // ✅ OK
processValue(42); // ✅ OK
processValue(true); // ❌ Ошибка

// T должен быть только массив
function getFirstElement<T extends any[]>(arr: T): T[0] {
  return arr[0];
}

getFirstElement([1, 2, 3]); // ✅ OK
getFirstElement("hello"); // ❌ Ошибка

Несколько условий

// T должен быть объектом И иметь свойство email
interface User {
  email: string;
}

function sendEmail<T extends User>(user: T): void {
  console.log("Sending to:", user.email);
}

sendEmail({ email: "alice@example.com" }); // ✅ OK
sendEmail({ name: "Bob" }); // ❌ Ошибка: нет email

Практический пример: сортировка объектов

// T должен иметь метод compareTo
interface Comparable {
  compareTo(other: Comparable): number;
}

function sort<T extends Comparable>(items: T[]): T[] {
  return items.sort((a, b) => a.compareTo(b));
}

class User implements Comparable {
  constructor(public name: string, public age: number) {}
  
  compareTo(other: User): number {
    return this.age - other.age;
  }
}

const users = [
  new User("Alice", 30),
  new User("Bob", 25)
];

sort(users); // ✅ Отсортирован по возрасту

Ограничение ключами объекта (keyof)

// K должен быть ключом объекта T
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "Alice", age: 30 };

getProperty(user, "name"); // ✅ OK, возвращает string
getProperty(user, "age");  // ✅ OK, возвращает number
getProperty(user, "email"); // ❌ Ошибка

// TypeScript точно знает возвращаемый тип!
const name: string = getProperty(user, "name"); // ✅ Правильный тип

Условные типы (Conditional Types)

// Если T это массив, вернуть элемент, иначе сам T
type Flatten<T> = T extends Array<infer U> ? U : T;

type StrResult = Flatten<string[]>; // string
type NumResult = Flatten<number>;   // number

// Практический пример: функция для работы с массивами и одиночными значениями
function processValue<T extends any[] | string>(value: T): T extends any[] ? T[0] : T {
  if (Array.isArray(value)) {
    return value[0] as any;
  }
  return value as any;
}

processValue(["a", "b"]); // ✅ Вернёт "a"
processValue("hello"); // ✅ Вернёт "hello"

React компонент с ограничениями

interface BaseProps {
  id: string;
  className?: string;
}

// Props должны расширять BaseProps
function Card<T extends BaseProps>(props: T) {
  return (
    <div id={props.id} className={props.className}>
      Содержимое карточки
    </div>
  );
}

// ✅ Работает - есть id
<Card id="card-1" className="p-4" />;

// ❌ Ошибка - отсутствует обязательное поле id
<Card className="p-4" />;

Чек листо типо-безопасности

  • Используй extends для ограничения Generic типов
  • Используй keyof для ограничения ключами объекта
  • Используй infer в условных типах для вывода типа
  • Всегда специфицируй ограничение если Generic может быть неправильным типом

Итог: Generic constraints делают код более безопасным, предотвращая ошибки типов на этапе разработки вместо runtime, и улучшают автодополнение в IDE.

Как ограничить Generic на входящие данные? | PrepBro