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

Универсальные шаблоны особенно полезны при работе со сложными структурами данных или API, которые должны обрабатывать несколько типов данных. Они могут помочь уменьшить количество ошибок и улучшить типобезопасность вашего кода.

В этой статье мы рассмотрим, как использовать дженерики TypeScript для повышения безопасности типов вашего кода. Мы начнем с простого примера универсальной функции, а затем перейдем к более сложным темам, таким как общие ограничения и общие классы.

Начнем с простого примера универсальной функции. Вот функция, которая принимает значение и возвращает массив с этим значением в качестве единственного элемента:

function singleton<T>(value: T): T[] {
  return [value];
}

Эта функция имеет параметр типа T, представляющий тип значения, которое она принимает в качестве аргумента. Параметр типа используется для указания типа возвращаемого значения функции, которая представляет собой массив элементов типа T.

Мы можем вызвать эту функцию с любым типом значения, и компилятор TypeScript выведет правильный тип для параметра типа:

const numbers = singleton(1);  // numbers: number[]
const strings = singleton('hello');  // strings: string[]

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

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

interface Container<T> {
  value: T;
}
const numberContainer: Container<number> = { value: 1 };
const stringContainer: Container<string> = { value: 'hello' };

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

interface HasName {
  name: string;
}
function greet<T extends HasName>(value: T): string {
  return `Hello, ${value.name}!`;
}
greet({ name: 'John' });  // returns: "Hello, John!"
greet({});  // error: Property 'name' is missing in type '{}'

В этом примере мы использовали универсальное ограничение, чтобы указать, что параметр типа T должен иметь свойство name типа string. Это гарантирует, что функцию greet можно вызывать только с объектами, имеющими свойство name, что повышает безопасность типов функции.

Я надеюсь, что эта статья помогла вам показать, как использовать дженерики TypeScript для повышения безопасности типов вашего кода. Чтобы узнать больше о дженериках и других дополнительных функциях TypeScript, я рекомендую изучить официальную документацию и онлайн-ресурсы. Удачного кодирования!