Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як працюють generic-класи і чому статичні члени не можуть використовувати параметр типу?

Клас, як і функція, може мати параметри типу - тоді один клас працює з різними типами даних зі збереженням перевірок.

class Repository<T extends { id: number }> {
  #items = new Map<number, T>();

  save(item: T): void {
    this.#items.set(item.id, item);
  }

  find(id: number): T | undefined {
    return this.#items.get(id);
  }

  all(): T[] {
    return [...this.#items.values()];
  }
}

const users = new Repository<User>();
users.save({ id: 1, name: 'Оля' });
users.find(1)?.name;   // тип User

Обмеження extends гарантує, що в T є id - інакше item.id всередині класу був би помилкою.

Виведення параметра типу з конструктора:

class Box<T> {
  constructor(public value: T) {}
}

const box = new Box(42);   // Box<number> - тип виведено

Чому статичні члени не можуть використовувати T:

class Repository<T> {
  static defaultItem: T;   // помилка: static members cannot reference class type parameters
}

Параметр типу належить екземпляру: new Repository<User>() і new Repository<Post>() - різні «версії» з різними T. А статичний член один на весь клас - для всіх екземплярів одразу. Яким має бути T у Repository.defaultItem? Відповіді немає.

Якщо статичному методу потрібен generic, він оголошує власний параметр типу:

class Repository<T> {
  static of<U extends { id: number }>(items: U[]): Repository<U> {
    const repo = new Repository<U>();
    items.forEach((item) => repo.save(item));
    return repo;
  }
}

Корисні прийоми:

  • значення за замовчуванням для параметра: class Cache<V = string>;
  • кілька параметрів: class Store<State, Action extends { type: string }>;
  • this як тип у методах - для ланцюжків, що зберігають тип нащадка.

Обмеження: параметри типу зникають під час виконання. Усередині класу не можна написати new T() чи x instanceof T - якщо потрібен конструктор, його передають явно: constructor(private make: new () => T).

Докладніше в документації: Generic-класи

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання