TypeScript Базовые обобщенные типы

Обобщённые типы или обобщения в TypeScript позволяют создавать классы, функции, интерфейсы и другие структуры, которые могут работать с разными типами данных без необходимости жёсткого указания конкретного типа.

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


Как образуется обобщённый тип

Обобщённый тип формируется добавлением параметров типа (буква или имя) после имени типа в угловых скобках <>. Эти параметры выступают как "заглушки", которые будут замещены настоящими типами при конкретном применении.

Пример


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

Здесь T — это параметр типа, который может быть замещён любым типом при вызове функции.


Использование обобщённого типа:

При использовании обобщённого типа, нужно указать конкретный тип, который заместит параметр типа.

Пример


identity<string>("Hello"); // "Hello"
identity<number>(42);     // 42

Здесь мы указали конкретный тип (string и number), и TypeScript применил его к обобщённому типу.


Функции

Обобщенные типы с функциями помогают создавать более общие функции, которые точно отражают типы входных и выходных данных.

Пример


function createPair<S, T>(v1: S, v2: T): [S, T] {
  return [v1, v2];
}

console.log(createPair<string, number>("hello", 42)); // ['hello', 42]

TypeScript также может автоматически выводить тип обобщения из параметров функции.


Классы

Обобщенные типы можно использовать для создания обобщённых классов.

Пример


class NamedValue<T> {
  private _value: T | undefined;

  constructor(private name: string) {}

  public setValue(value: T) {
    this._value = value;
  }

  public getValue(): T | undefined {
    return this._value;
  }

  public toString(): string {
    return `${this.name}: ${this._value}`;
  }
}

let value = new NamedValue<number>("myNumber");
value.setValue(10);
console.log(value.toString()); // myNumber: 10

TypeScript также может автоматически выводить тип обобщенного параметра, если он используется в параметре конструктора.


Псевдонимы типов

Обобщения в псевдонимах типов позволяют создавать более повторно используемые типы.

Пример


type Wrapped<T> = { value: T };

const wrappedValue: Wrapped<number> = { value: 10 };

То же самое можно сделать с интерфейсами, используя следующий синтаксис interface Wrapped<T> {.


Значения по умолчанию

Обобщениям можно задавать значения по умолчанию, которые применяются, если никакое другое значение не указано или не выведено.

Пример


class NamedValue<T = string> {
  private _value: T | undefined;

  constructor(private name: string) {}

  public setValue(value: T) {
    this._value = value;
  }

  public getValue(): T | undefined {
    return this._value;
  }

  public toString(): string {
    return `${this.name}: ${this._value}`;
  }
}

let value = new NamedValue("myNumber");
value.setValue("myValue");
console.log(value.toString()); // myNumber: myValue


Ограничения

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

Пример


function logAndReturn<T extends string | number>(value: T): T {
  console.log(value);
  return value;
}

logAndReturn("Hello"); // разрешено
logAndReturn(42);      // разрешено
logAndReturn(true);    // ошибка, так как boolean не соответствует ограничениям

Ограничения можно сочетать с использованием значений по умолчанию.