Каково общее использование дженериков в Typescript? - PullRequest
0 голосов
/ 21 апреля 2020

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

Но, возьмите следующий пример:

interface Identities<V, W> {
   id1: V,
   id2: W
}

function identities<T, U> (arg1: T, arg2: U): Identities<T, U> {   
 let identities: Identities<T, U> = {
    id1: arg1,
    id2: arg2
  };
  return identities;
}

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

Я могу представить, если вы хотите, чтобы ваша функция / класс могла вместить множество различных аргументов, но единственным ограничением было бы то, что они должны быть совместимы с определенными методами:

interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

это используется в определенных ситуациях.

Ответы [ 2 ]

1 голос
/ 23 апреля 2020

Термин "информатика" для Generics равен Parametri c Polymorphism , и он на самом деле довольно хорошо показывает, для чего они полезны. (Хорошо, если вы знаете, что это значит.)

Итак, что означает это значит?

Хорошо, давайте просто посмотрим, что означает "параметри c" В основном. Это просто означает, что у нас могут быть параметры. Я думаю, что мы все можем согласиться, что параметры полезны. Функция, которая может добавлять только 3 и 5, довольно скучная. Функция, которая может добавлять любое число к любому числу и может быть параметризована по этим числам, теперь это гораздо полезнее!

A конструктор типа (вот что такое тип generi c называется в информатике) создает тип из другого типа. Имя «конструктор типов» было выбрано потому, что в некоторых разделах математики функции фактически называются «конструкторами» или «конструкторами значений» (поскольку они создают новые выходные значения на основе входных значений), а конструктор типов очень похож, за исключением того, что на уровне значения, но на уровне типа .

Например, конструктор типа Promise принимает один аргумент, скажем, number и создает из этого нового типа , который является «NumberPromise».

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

И почему, в частности, параметри c полиморфизм полезен? Т.е. полиморфизм, при котором параметр типа совершенно неизвестен?

Помните, что вообще означает полиморфизм : фрагмент кода, способный делать вещи с разными типами.

Наиболее общеизвестной формой полиморфизма является ad-ho c полиморфизм , где код делает разные вещи для каждого типа и должен иметь специфицированную c, отдельную реализацию для каждого типа он хочет иметь дело с. Наиболее широко используемая реализация полиморфизма ad-ho c - это объектно-ориентированная диспетчеризация виртуального метода, где каждый класс имеет свою собственную независимую реализацию метода.

(В действительности OOP обычно использует комбинация полиморфизма подтипа и полиморфизма ad-ho * 1106. * Кроме того, он поддерживает дифференциальное повторное использование кода , когда реализация подтипа должна реализовывать только то, что он хочет сделать иначе , но может повторно использовать реализацию супертипа, например, через наследование или делегирование прототипа.)

В параметризации c полиморфизм, OTOH, есть только одиночная операция , которая работает для всех типов . Типичным примером для начинающих является функция, которая возвращает длину односвязного списка. На самом деле он не должен знать о типе элементов, поэтому он может быть параметри c в типе элемента, что-то вроде этого:

function length<T>(list: List<T>) {
  return list.rest === undefined ? 0 : 1 + length(list.rest);
}

Эта функция делает ничего не делать с элементами списка, поэтому он может быть generi c над типом элемента.

Другим примером является функция идентификации :

function id<T>(it: T) {
  return it;
}

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

Однако существуют функции, которые должны что-то знать о типах. Например, функция sort для списков должна знать, что элементы сопоставимы. Нам нужно поставить определенные «границы» вокруг параметров типа, это называется ограниченный полиморфизм :

interface Comparable<T> {
  const enum Comparing {
    LessThan = -1,
    Equal,
    GreaterThan
  }

  compareTo(other: T): Comparing
}

function sort<T extends Comparable<T>>(list: List<T>) {
  // do some sorting
}

Относительно вашего вопроса о моделировании их с типами объединения и пересечения, принципиальная разница здесь является то, что с параметри c полиморфизм , тип является аргумент , который предоставляется пользователь . Если вы хотите, например, Чтобы смоделировать класс коллекции generi c, используя объединение всех возможных типов элементов, вы должны знать каждый возможный тип элемента, который может быть заранее написан кем-либо на pl anet, до ты пишешь сборник! Это явно невозможно. Вы никогда не сможете перечислить все возможные типы, которые можно поместить в список, на карту или в массив.

Или подумайте о типах функций: это было бы очень ограничительно, если бы функции работали только с указанными c исчерпывающее объединение типов, которые дизайнеры TypeScript вкладывают в определение, не правда ли?

Вот ссылка на вопрос Software Engineering, который освещает некоторые аспекты в другом направлении. ОП задан в контексте C#, который является более строгим, чем TypeScript, поскольку он не поддерживает типы объединения или пересечения, но я думаю, что обсуждение все равно будет полезно для вас:

1 голос
/ 21 апреля 2020

Обобщения могут использоваться для хранения дополнительных данных из API-вызовов, например:

Ответ API:

value: {} // this is generic
messages: [] // additional logging/errors/warnings
// other properties that apply for all objects

Машинопись:

export interface GenericResponse<T> {
  value: T;
  messages: ApiMessageItem[];
 // additional properties
}

, где T это тот объект, который я вызвал из моего API.

Вы все еще можете сделать это с помощью Unions, но я думаю, что лучше использовать дженерики, особенно с 50 различными объектами API.

...