Gernar
TypeScript

Как ограничить Generic в TypeScript

Разбор вопроса «Как ограничить Generic в TypeScript» для Frontend Developer: что проверяет интервьюер, ключевые тезисы, практические примеры и частые ошибки.

Вопрос

Как ограничить Generic в TypeScript

Профессия

Frontend Developer

Что хочет услышать интервьюер

Интервьюер хочет услышать, что кандидат понимает, как ограничивать Generic типы для повышения безопасности типов и предсказуемости кода. Также важно показать практический пример использования.

Ключевые тезисы

  • Использование ключевого слова extends для ограничения Generic типами, интерфейсами или примитивами.
  • Пример: <T extends string> означает, что Generic T может быть только строкой или её подтипом.
  • Можно ограничивать несколькими типами через пересечение: <T extends string & { length: number }>.
  • Ограничение Generic помогает избежать ошибок типизации и делает код более предсказуемым.

Подробный ответ

В TypeScript Generic (обобщённые типы) позволяют создавать гибкие и переиспользуемые компоненты, которые могут работать с различными типами данных. Однако иногда требуется ограничить диапазон типов, которые может принимать Generic, чтобы избежать ошибок и сделать код более предсказуемым. Для этого используется ключевое слово extends. Оно позволяет указать, что Generic должен быть определённого типа или удовлетворять определённым условиям. Например, <T extends string> означает, что Generic T может быть только строкой или её подтипом. Это полезно, когда функция или класс должны работать только с конкретными типами данных. Также можно использовать пересечение типов, чтобы ограничить Generic несколькими условиями одновременно. Например, <T extends string & { length: number }> означает, что T должен быть строкой и одновременно иметь свойство length. Ограничение Generic помогает избежать ошибок типизации и делает код более предсказуемым.

Практические примеры

Пример 1

Ограничение Generic строкой. function logLength<T extends string>(arg: T): void {
  console.log(arg.length); } logLength('hello'); // Работает logLength(42); // Ошибка: аргумент не является строкой

Пример 2

Ограничение Generic объектом с определённым свойством. interface HasId { id: number;
}
function printId<T extends HasId>(obj: T): void {
  console.log(obj.id); } printId({ id: 1, name: 'John' }); // Работает printId({ name: 'Jane' }); // Ошибка: объект не имеет свойства id

Пример 3

Ограничение Generic классом с определённым методом. class Animal { makeSound(): void {
  console.log('Some sound'); }
}
function makeSound<T extends Animal>(animal: T): void { animal.makeSound(); } makeSound(new Animal()); // Работает makeSound({}); // Ошибка: объект не имеет метода makeSound

Пример 4

Использование Utility типов для ограничения Generic. function updatePartial<T, K extends keyof T>(obj: T, key: K, value: T[K]): void { obj[key] = value; } updatePartial({ name: 'John', age: 30 }, 'age', 31); // Работает updatePartial({ name: 'John', age: 30 }, 'height', 180); // Ошибка: ключ 'height' не существует в объекте

Частые ошибки

  • Типичная ошибка: Попытка использовать Generic без ограничений, когда это необходимо. Это может привести к ошибкам типизации, если функция или класс получают неподходящий тип данных.
  • Ошибка: Неправильное использование пересечения типов. Например, &lt;T extends string & number&gt; невозможно, так как тип не может быть одновременно строкой и числом.

Связанные темы

  • Utility типы в TypeScript, такие как Partial, Pick, Omit и другие.
  • Интерфейсы и типы в TypeScript, их различия и использование.
  • Наследование и реализация интерфейсов в TypeScript.
  • Работа с классами и методами в TypeScript.

Follow-up вопросы

Можно ли ограничить Generic так, чтобы он принимал только объекты с определённым свойством?

Уровень: basic

Да, можно использовать extends с интерфейсом или типом, описывающим нужное свойство. Например: &lt;T extends &#123; id: number &#125;&gt; — Generic T должен содержать свойство id типа number.

Как ограничить Generic, чтобы он работал только с классами, имеющими определённый метод?

Уровень: intermediate

Для этого используется extends с интерфейсом, описывающим метод. Например: &lt;T extends &#123; toString(): string &#125;&gt; — Generic T должен иметь метод toString, возвращающий string.

Как использовать Utility типы (например, Partial или Pick) для ограничения Generic?

Уровень: intermediate

Utility типы можно комбинировать с extends. Например: &lt;T extends Partial&lt;User&gt;&gt; — Generic T должен быть частичной версией типа User. Это полезно для валидации частичных данных.

Можно ли создать Generic, который ограничен только типами, имеющими конструктор?

Уровень: advanced

Да, с помощью new (...args: any[]) =&gt; any. Например: &lt;T extends new (...args: any[]) =&gt; any&gt; — Generic T должен быть конструктором. Это полезно для фабричных функций.

Как ограничить Generic так, чтобы он принимал только типы, совместимые с другим Generic?

Уровень: advanced

Можно использовать extends с другим Generic. Например: &lt;T extends U, U&gt; — Generic T должен быть подтипом U. Это полезно для функций, работающих с наследниками определённого типа.

Содержание