TypeScript - Ж_generics: Введение для начинающих

Привет, будущий супергерой кодирования! Сегодня мы отправляемся в увлекательное путешествие в мир обобщённых типов TypeScript. Не волнуйтесь, если вы новички в программировании – я буду вашим дружелюбным проводником, и мы будем двигаться шаг за шагом. К концу этого руководства вы будете использовать обобщённые типы, как профи!

TypeScript - Generics

Что такое обобщённые типы и почему нам это важно?

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

Примеры проблем

Давайте рассмотрим несколько сценариев, где обобщённые типы могут спасти положение:

  1. Вы хотите создать функцию, которая может反转 любой тип массива (числа, строки, объекты).
  2. Вам нужна класс, который может хранить и извлекать любой тип данных.
  3. Вы разрабатываете утилиту, которая должна работать с различными типами данных.

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

TypeScript обобщённые типы на выручку!

Теперь, давайте натянем рукава и посмотрим, как работают обобщённые типы на практике.

Основная функция обобщённого типа

Вот пример простой функции обобщённого типа, которая может работать с любым типом:

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

Давайте разберём это:

  • <T> – это наш параметр типа. Это как placeholder для типа, который мы будем использовать.
  • (arg: T) означает, что наша функция принимает аргумент типа T.
  • : T после скобок означает, что наша функция вернёт значение типа T.

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

let output1 = identity<string>("Привет, Обобщённые типы!");
let output2 = identity<number>(42);

console.log(output1); // "Привет, Обобщённые типы!"
console.log(output2); // 42

Круто, правда? Одна и та же функция работает с различными типами!

Обобщённый интерфейс

Мы также можем использовать обобщённые типы с интерфейсами. Вот пример:

interface GenericBox<T> {
contents: T;
}

let stringBox: GenericBox<string> = { contents: "Секретное сообщение" };
let numberBox: GenericBox<number> = { contents: 123 };

console.log(stringBox.contents); // "Секретное сообщение"
console.log(numberBox.contents); // 123

Наш GenericBox может удерживать любой тип содержимого. Это как та магическая коробка, о которой мы говорили раньшe!

Обобщённые классы

Давайте создадим обобщённый класс, который может работать в качестве простого хранилища данных:

class DataStore<T> {
private data: T[] = [];

addItem(item: T): void {
this.data.push(item);
}

getItems(): T[] {
return this.data;
}
}

let stringStore = new DataStore<string>();
stringStore.addItem("Привет");
stringStore.addItem("Мир");
console.log(stringStore.getItems()); // ["Привет", "Мир"]

let numberStore = new DataStore<number>();
numberStore.addItem(1);
numberStore.addItem(2);
console.log(numberStore.getItems()); // [1, 2]

Этот класс DataStore может хранить и извлекать любой тип данных. Pretty handy, huh?

Ограничения обобщённых типов

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

interface Lengthy {
length: number;
}

function logLength<T extends Lengthy>(arg: T): void {
console.log(arg.length);
}

logLength("Привет"); // 5
logLength([1, 2, 3]); // 3
logLength({ length: 10 }); // 10
// logLength(123); // Error: Number doesn't have a length property

Здесь наша функция logLength может работать только с типами, которые имеют свойство length.

Преимущества обобщённых типов

Теперь, когда мы видели обобщённые типы в действии, lets подвести их преимущества:

  1. Повторное использование кода: Написать один раз, использовать с множеством типов.
  2. Типовая безопасность: Catch ошибки, связанные с типом, на этапе компиляции.
  3. Гибкость: Создавать компоненты, которые могут работать с различными типами данных.
  4. Ясность: Делать очевидными взаимоотношения между входами и выходами.

Таблица методов обобщённых типов

Вот удобная таблица некоторых часто встречающихся обобщённых методов:

Метод Описание Пример
Array.map<U>() Преобразует элементы массива [1, 2, 3].map<string>(n => n.toString())
Promise.all<T>() Ждёт завершения всех обетов Promise.all<number>([Promise.resolve(1), Promise.resolve(2)])
Object.keys<T>() Получает ключи объекта в массиве Object.keys<{name: string}>({name: "Alice"})
JSON.parse<T>() Пarsed JSON строку в объект JSON.parse<{age: number}>('{"age": 30}')

Заключение

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

Пока вы продолжаете своё путешествие в программировании, вы обнаружите, что обобщённые типы – это как швейцарский армейский нож в вашем наборе инструментов TypeScript – универсальный, мощный и невероятно полезный. Так что前进 и кодируйте, молодой падаван, и пусть обобщённые типы будут с вами!

Счастливого кодирования, и до следующего раза, продолжайте исследовать и учиться!

Credits: Image by storyset