【问题标题】:What are the common usage of Generics in Typescript?Typescript 中泛型的常见用法是什么?
【发布时间】:2020-08-03 17:53:48
【问题描述】:

我试图了解 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;
}

这在某些情况下使用。

【问题讨论】:

  • 您在寻找示例吗?一个常见的例子是。对于 React,当我们将接口/类型别名定义为组件的泛型类型时。
  • 在创建框架时很有用,例如@wentjun 提到的。此外,当您尝试定义自己的数组函数(例如合并排序)时,它也很有用。

标签: javascript typescript typescript-generics


【解决方案1】:

泛型的“计算机科学”术语是参数多态性,它实际上很好地展示了它们的用途。 (好吧,如果你知道这意味着什么。)

那么,是什么意思

好吧,让我们看看“参数”的一般含义。这只是意味着我们可以有参数。我想我们都同意参数是有用的。只能加 3 和 5 的函数很无聊。一个可以将任何数字添加到任何数字并且可以对这些数字进行参数化的函数,现在这更有用!

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

例如,Promise 类型构造函数采用一个参数,假设为 number,并从中构造一个 新类型,即“NumberPromise”。

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

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

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

最广为人知的多态形式是即席多态,其中代码为每种类型做不同的事情,并且必须为每个它要处理的类型。 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;
}

这些函数都是完全参数化,即它们适用于您输入的任何类型参数。事实上,它们甚至根本不关心类型.

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

关于您关于使用联合和交集类型建模的问题,这里的根本区别在于,对于 参数多态性,类型是由 参数提供的 参数 em>用户。如果你想例如通过使用所有可能元素类型的联合来建模通用集合类,在编写集合之前,您必须提前知道地球上任何人可能编写的每个可能的元素类型!这显然是不可能的。您将永远无法列出可以放入列表、映射或数组中的所有可能类型。

或者,想想函数类型:如果函数只能与 TypeScript 设计者在定义中放入的特定详尽类型联合工作,那将是非常有限制的,你不觉得吗?

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我是 typescript 的新手,你能解释一下这个 T 与 any 有何不同吗?
    • 也许this 澄清了我的意思。基本上不要使用任何,除非你真的不能确定类型(在这种情况下,首先考虑未知和联合)。
    • 所以泛型更像是在调用函数时确定参数的类型,而不是预先确定它们?
    • 泛型当然不止于此,但是是的,您可以使用它们。考虑阅读 typescriptlang.org/docs/handbook/generics.html 以获取有关 typescript 泛型的更多信息。
    猜你喜欢
    • 1970-01-01
    • 2017-07-25
    • 2019-04-02
    • 2019-03-08
    • 2021-09-25
    • 2015-11-25
    • 1970-01-01
    • 2021-02-11
    相关资源
    最近更新 更多