JavaRush /Курсы /Модуль 2: Fullstack /Дженерики (Generics)

Дженерики (Generics)

Модуль 2: Fullstack
6 уровень , 1 лекция
Открыта

2.1 Введение в дженерики

Дженерики (Generics) в TypeScript — это мощный инструмент, который позволяет создавать компоненты, способные работать с различными типами данных, обеспечивая при этом строгую типизацию. Это достигается путем определения типов-параметров, которые можно заменить конкретными типами во время использования.

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

Синтаксис дженериков

Основной синтаксис дженериков в TypeScript включает использование угловых скобок (<>) для определения параметров типов.

    
      function name<T>(arg: T): T {
      }
    
  

Также возможны варианты:

  • в виде типа-параметра передается тип возвращаемого значения:
            
              function name<T>(): T {
              }
            
          
  • в виде типа-параметра передается тип параметра функции:
            
              function name<T>(param:T) {
              }
            
          
  • или несколько параметров:
            
              function name<T1, T2, T3>(param1:T1, param2:T2, param3:T3) {
              }
            
          

Пример:

JavaScript
    
      "use strict";
      function identity(arg) {
          return arg;
      }
      let output1 = identity("Hello, TypeScript!");
      let output2 = identity(42);
      console.log(output1); // Вывод: Hello, TypeScript!
      console.log(output2); // Вывод: 42
    
  
TypeScript
    
      function identity<T>(arg: T): T {
        return arg;
      }

      let output1 = identity<string>("Hello, TypeScript!");
      let output2 = identity<number>(42);

      console.log(output1); // Вывод: Hello, TypeScript!
      console.log(output2); // Вывод: 42
    
  

Здесь функция identity принимает параметр типа T и возвращает значение того же типа. Параметр типа T заменяется конкретным типом при вызове функции.

2.2 Базовые примеры использования дженериков

Дженерики в функциях

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

Пример:

JavaScript
    
      "use strict";
      function reverseArray(items) {
          return items.reverse();
      }
      let numbers = [1, 2, 3, 4];
      let reversedNumbers = reverseArray(numbers);
      console.log(reversedNumbers); // Вывод: [4, 3, 2, 1]
      let strings = ["one", "two", "three"];
      let reversedStrings = reverseArray(strings);
      console.log(reversedStrings); // Вывод: ["three", "two", "one"]
    
  
TypeScript
    
      function reverseArray<T>(items: T[]): T[] {
        return items.reverse();
      }

      let numbers = [1, 2, 3, 4];
      let reversedNumbers = reverseArray(numbers);
      console.log(reversedNumbers); // Вывод: [4, 3, 2, 1]

      let strings = ["one", "two", "three"];
      let reversedStrings = reverseArray(strings);
      console.log(reversedStrings); // Вывод: ["three", "two", "one"]
    
  

Здесь функция reverseArray принимает массив элементов типа T и возвращает массив того же типа, но в обратном порядке.

Дженерики в интерфейсах

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

Пример:

JavaScript
    
      "use strict";
      let pair = {
          first: "Age",
          second: 30
      };
      console.log(pair.first); // Вывод: Age
      console.log(pair.second); // Вывод: 30
    
  
TypeScript
    
      interface Pair<T, U> {
        first: T;
        second: U;
      }

      let pair: Pair<string, number> = {
        first: "Age",
        second: 30
      };

      console.log(pair.first); // Вывод: Age
      console.log(pair.second); // Вывод: 30
    
  

Здесь интерфейс Pair принимает два параметра типа T и U, которые определяют типы его свойств first и second.

Дженерики в классах

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

Пример:

JavaScript
    
      "use strict";
      class Stack {
          constructor() {
              this.items = [];
          }
          push(item) {
              this.items.push(item);
          }
          pop() {
              return this.items.pop();
          }
      }
      let numberStack = new Stack();
      numberStack.push(10);
      numberStack.push(20);
      console.log(numberStack.pop()); // Вывод: 20
      let stringStack = new Stack();
      stringStack.push("A");
      stringStack.push("B");
      console.log(stringStack.pop()); // Вывод: B
    
  
TypeScript
    
      class Stack<T> {
        private items: T[] = [];

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

        pop(): T | undefined {
          return this.items.pop();
        }
      }

      let numberStack = new Stack<number>();
      numberStack.push(10);
      numberStack.push(20);
      console.log(numberStack.pop()); // Вывод: 20

      let stringStack = new Stack<string>();
      stringStack.push("A");
      stringStack.push("B");
      console.log(stringStack.pop()); // Вывод: B
    
  

Здесь класс Stack использует дженерик T для определения типа элементов, которые он может содержать. Это позволяет создавать стеки для различных типов данных, сохраняя при этом типовую безопасность.

2.3 Примеры

Дженерики широко используются в реальных проектах для создания универсальных и типобезопасных компонентов. Рассмотрим несколько примеров.

Пример 1: Функция фильтрации массива

JavaScript
    
      "use strict";
      function filterArray(array, predicate) {
          return array.filter(predicate);
      }
      let numbers = [1, 2, 3, 4, 5];
      let evenNumbers = filterArray(numbers, num => num % 2 === 0);
      console.log(evenNumbers); // Вывод: [2, 4]
      let words = ["apple", "banana", "cherry"];
      let filteredWords = filterArray(words, word => word.includes("a"));
      console.log(filteredWords); // Вывод: ["apple", "banana"]
    
  
TypeScript
    
      function filterArray<T>(array: T[], predicate: (item: T) => boolean): T[] {
        return array.filter(predicate);
      }

      let numbers = [1, 2, 3, 4, 5];
      let evenNumbers = filterArray(numbers, num => num % 2 === 0);
      console.log(evenNumbers); // Вывод: [2, 4]

      let words = ["apple", "banana", "cherry"];
      let filteredWords = filterArray(words, word => word.includes("a"));
      console.log(filteredWords); // Вывод: ["apple", "banana"]
    
  

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

Пример 2: Словарь с дженериками

JavaScript
    
      "use strict";
      class Dictionary {
        constructor() {
          this.items = {};
        }
        setItem(key, value) {
          this.items[key] = value;
        }
        getItem(key) {
          return this.items[key];
        }
      }
      let dictionary = new Dictionary();
      dictionary.setItem("one", 1);
      dictionary.setItem("two", 2);
      console.log(dictionary.getItem("one")); // Вывод: 1
      console.log(dictionary.getItem("three")); // Вывод: undefined
    
  
TypeScript
    
      class Dictionary<TKey, TValue> {
        private items: { [key: string]: TValue } = {};

        setItem(key: TKey, value: TValue): void {
          this.items[key as any] = value;
        }

        getItem(key: TKey): TValue | undefined {
          return this.items[key as any];
        }
      }

      let dictionary = new Dictionary<string, number>();
      dictionary.setItem("one", 1);
      dictionary.setItem("two", 2);

      console.log(dictionary.getItem("one")); // Вывод: 1
      console.log(dictionary.getItem("three")); // Вывод: undefined
    
  

Здесь класс Dictionary использует два параметра типа TKey и TValue для определения типов ключей и значений словаря. Это позволяет создавать словари для различных типов данных.

Преимущества использования дженериков

  1. Типобезопасность: дженерики обеспечивают строгую типизацию, что помогает обнаруживать ошибки на этапе компиляции.
  2. Гибкость и универсальность: дженерики позволяют создавать универсальные функции, классы и интерфейсы, которые могут работать с различными типами данных.
  3. Повторное использование кода: использование дженериков позволяет избегать дублирования кода, создавая универсальные компоненты, которые можно использовать в различных контекстах.
  4. Улучшенная читаемость и поддерживаемость: дженерики делают код более понятным и легко поддерживаемым, так как они четко определяют типы данных, с которыми работают компоненты.
3
Задача
Модуль 2: Fullstack, 6 уровень, 1 лекция
Недоступна
Функция поиска элемента
Функция поиска элемента
3
Задача
Модуль 2: Fullstack, 6 уровень, 1 лекция
Недоступна
Фильтрация объектов по свойству
Фильтрация объектов по свойству
3
Задача
Модуль 2: Fullstack, 6 уровень, 1 лекция
Недоступна
Универсальный стек
Универсальный стек
3
Задача
Модуль 2: Fullstack, 6 уровень, 1 лекция
Недоступна
Обратный словарь **
Обратный словарь **
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ