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

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

Например,

enum Subject = {
	Maths = 'Maths',
	English = 'English',
	Deutsch = 'Deutsch'
}

type Book = {
 subject: Subject,
 author: string,
 year: number
}

/** 
 this function is expected to receive math books only, both book can be 
 Maths, Deutsch or English
*/ 
function takeMathsBookOnly(book: Book) {
 
}

В приведенном выше фрагменте кода у нас есть тип Book со свойством subject enum Subject, которое может быть Maths, English или Deutsch, а также функция takeMathsBookOnly, которая ожидает получать только книги по математике. Однако книги, как мы знаем, могут быть любой тематики.

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

enum Subject = {
	Maths = 'Maths',
	English = 'English',
	Deutsch = 'Deutsch'
}
	
// Default parameter = Subject, meaning Book
type Book<T = Subject> = {
	subject: T, // subject: Subject
	author: string,
	year: number
}
type MathBook = Book<Subject.Maths> 
/** subject can only be equal to 'Maths'
 {
  subject: Subject.Maths,
  author: string,
  year: number
 }
**/

type EnglishBook = Book<Subject.English> 
/** subject can only be equal to 'English'
 {
  subject: Subject.English,
  author: string,
  year: number
 }
**/

type DeutschBook = Book<Subject.Deutsch>
/** subject can only be equal to 'Deutsch'
 {
  subject: Subject.Deutsch,
  author: string,
  year: number
 }
**/

function takeMathsBookOnly(book: MathBook) {
 
}

Из приведенного выше фрагмента Book теперь принимает переменную универсального типа с именем T , T точно так же, как параметры функции принимают Subject в качестве типа по умолчанию, поэтому тип Book такой же, как и предыдущая функциональность, однако дополнительное преимущество заключается в том, что теперь мы можем повторно использовать Book в разных контекстах, мы можем создать MathBook тип, только передав Subject.Maths в качестве аргумента Book , поэтому в случае T = Subject.Maths . Мы можем создать столько конкретных типов, сколько захотим, используя дженерики.

Другой общий вариант использования

// generic map function
const map  = <T, U>(items: T[], fn: (item: T) => U): U[] => {
	const result: U[] = [];
	for (let item of items) {
		result.push(fn(item));
	}
	return result;
}

const doubleNumbers = map<number, number>([1, 2, 3, 4, 5], (item) => item * 2); 
// [2, 4, 6, 8, 10]

const stringifyDoubleNumbers = map<number, string>([1, 2, 3, 4, 5], (item) => `${item * 2}`); 
// ['2', '4', '6', '8', '10']

Давайте рассмотрим еще один общий вариант использования, в приведенном выше фрагменте у нас есть общая функция map с двумя переменными общего типа T и U , которая принимает items , являющуюся массивом T , и функция обратного вызова fn, которая принимает элемент типа T и возвращает U , иногда U может быть таким же, как T как в doubleNumbers , но это не всегда так, нам может понадобиться преобразовать элементы в другой тип, как в stringifyDoubleNumbers который получает массив number , но возвращает массив string .

Чтобы узнать больше о дженериках, ознакомьтесь с разделом Typescript Generics. Спасибо