打字稿中泛型的常见用法是什么?

时间:2020-04-21 04:14:05

标签: javascript typescript typescript-generics

我试图了解在现实生活中通常使用的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 个答案:

答案 0 :(得分:1)

泛型可用于存储来自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对象。

答案 1 :(得分:1)

泛型的“计算机科学”一词是参数多态性,它实际上很好地证明了它们的用处。 (好吧,如果您知道这意味着什么。)

那么, 是什么意思?

好吧,让我们看看“参数”的一般含义。这只是意味着我们可以有参数。我认为我们都可以同意参数是有用的。只能加3和5的函数很无聊。可以在任何数字上加上任何数字并可以在这些数字上进行参数化的函数,现在它变得更加有用!

类型构造器(这是计算机科学中称为泛型的类型)从另一个类型构造一个类型。之所以选择名称“类型构造函数”,是因为在某些数学分支中,函数实际上称为“构造函数”或“值构造函数”(因为它们基于输入值构造新的输出值),并且类型构造函数非常相似,除了在价值级别但在类型级别

例如,Promise类型的构造函数采用一个参数,比方说number,并从中构造一个 new类型,即“ NumberPromise”。

因此,带参数的类型构造函数在类型级别上有用的原因与带参数的函数在值级别上有用的原因相同:它们使您可以从现有事物中构造新事物。

为什么参数多态性特别有用?即类型参数完全未知的多态性?

记住多态性的一般含义:一段代码能够处理不同类型的事情。

最著名的多态形式是 ad-hoc多态,其中代码对每种类型执行不同的事情,并且每种代码必须有一个特定的单独实现输入要处理的内容。临时多态性最广泛使用的实现是面向对象的虚拟方法分派,其中每个类都有自己的独立方法实现。

(实际上,OOP通常使用子类型多态性和即席多态性的组合。此外,它支持差分代码重用,其中仅实现子类型需要实现不同的目标,但是可以重用超类型的实现,例如通过继承或原型委派。)

在参数多态性OTOH中,只有单个操作适用于所有类型。一个典型的初学者示例是一个函数,该函数返回单链列表的长度。实际上,它不需要了解元素的类型,因此它可以是元素类型中的 parametric ,如下所示:

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

此函数对列表的元素不执行任何操作,因此可以对元素类型进行泛型

另一个例子是身份函数

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

这些函数都是完全参数化的,即它们适用于您输入的 any 类型参数。实际上,它们甚至根本不在乎类型。

但是,有些函数需要了解有关类型的一些知识。例如,列表的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
}

关于使用联合和交集类型进行建模的问题,这里的根本区别在于,使用参数多态性,该类型是 argument ,由< em>用户。如果您想通过使用所有可能的元素类型的并集为通用集合类建模,在编写集合之前,您必须事先知道地球上任何人可能写的所有可能的元素类型。这显然是不可能的。您将永远无法列出可以放入列表,地图或数组中的每种可能的类型。

或者,考虑一下函数类型:如果函数仅使用TypeScript设计者在定义中放入的特定的详尽类型联合,那将是非常严格的限制,

这里是指向软件工程问题的链接,该问题从另一个方向阐明了某些方面。 OP在C#上下文中要求使用,它比TypeScript更具限制性,因为它不支持联合或交集类型,但我认为该讨论对您仍然有用:

  • Generics vs common interface:泛型对集合以外的其他东西有用吗?为什么我不能只声明我的集合包含单个通用类型的实例?