【问题标题】:Typescript "Generics" Syntax + Angle Brackets打字稿“泛型”语法+尖括号
【发布时间】:2020-08-21 03:32:04
【问题描述】:

我在理解 Typescript 中尖括号背后的用途时遇到了一些麻烦。

它们通常用于泛型。这对我来说是最清楚的功能。例如,如果您有一个想要使其更可重用(或“通用”)的函数,您可以在函数名称之后应用它们,如下所示:

function identity<T>(arg: T): T { 
  return arg; 
};

这会强制执行类型安全,同时还允许您将此函数用于多种类型。

但是,在其他情况下,&lt;&gt; 括号出现在其他类型之后,例如在以下事件处理程序中:

const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
  const val = e.currentTarget.value;
  setWord(val);
};

通过查看 TypeScript 的自动完成功能,React.ChangeEvent 显示:React.ChangeEvent&lt;Element&gt;

  1. 在这种情况下,&lt;Element&gt; 是什么意思?也是泛型的吗?
  2. 我们是否传入了HTMLInputElement,如果是,那是一个接口吗?
  3. 最后,为什么在这种情况下泛型(如果发生这种情况) React.ChangeEvent 类型之后传递,而对于函数,T 泛型在之前传递 函数体?

【问题讨论】:

    标签: javascript typescript casting


    【解决方案1】:

    React.ChangeEvent&lt;HTMLInputElement&gt; 语法显示了所谓的Generic Type。此类型接收另一种类型作为“参数”以进一步更好地指定它。

    将泛型类型与函数类似,但针对类型。我们用来接收一个或多个值并产生另一个值的函数。泛型类型是相似的,它们接收一个或多个类型并产生一个类型。仅泛型类型可能不足以表示某些东西,这取决于它是否使用可选参数声明(就像函数可能有也可能没有可选参数一样)。

    这是一个更简单的泛型类型示例:Array&lt;T&gt;。这只是T[] 的另一种说法。例如,Array&lt;number&gt;。另一个非常常见的泛型类型是Promise&lt;T&gt;,它表示解析为T 的promise 的类型。

    Promise 是一个很好的泛型类型示例,其参数是必需的

    // --------------------------------------
    // | interface Promise<T>               |
    // --------------------------------------
    // | Represents the completion of an    |
    // | asynchronous operation             |
    // --------------------------------------
    // | Generic type 'Promise<T>' requires |
    // | 1 type argument(s).   ts(2314)     |
    // --------------------------------------
    //          ^
    //          |
    //          |
    type X = Promise;
    

    假设你想用{ foo: something } 的形式表示一个对象,其中的东西可以是你想要的任何东西。您可以使用泛型类型来执行此操作:

    type Foo<SomeType> = {
      foo: SomeType;
    };
    
    // Now you can declare specific types
    
    type FooWithString = Foo<string>;
    // equivalent of:
    // type FooWithString = { foo: string };
    
    type FooWithNumber = Foo<number>;
    // equivalent of:
    // type FooWithString = { foo: number };
    

    如果我们想让这个SomeType 成为一个可选的类型参数,这样我们的泛型类型Foo 也可以单独使用(不需要通过尖括号传递类型),我们只需要决定我们想要什么默认类型为(类似于我们如何为普通函数选择默认值):

    type Foo<SomeType = Date> = {
      foo: SomeType;
    };
    

    现在我们可以使用不带尖括号的Foo - 这相当于使用Foo&lt;Date&gt;


    您可以在TypeScript reference for Utility Types 中找到几个有趣的此类类型示例。


    明确回答您的问题:

    1. &lt;Element&gt; 显示您悬停的泛型类型(即React.ChangeEvent)接收一个类型参数,并且创建此泛型类型的 React 团队的人决定调用它参数Element

    • 在我上面的示例中,如果您将鼠标悬停在 Foo&lt;number&gt; 上,它将显示 Foo&lt;SomeType&gt;,因为这就是我声明类型的方式。

    2a. 是的,我们将HTMLInputElement 作为参数传递给泛型类型React.ChangeEvent。将React.ChangeEvent 想象成一个“类型函数”,它接受一个类型参数并“返回”另一种类型。所以我们将HTMLInputElement 赋予React.ChangeEvent,这样结果类型React.ChangeEvent&lt;HTMLInputElement&gt; 就指定了我们想要的。

    2b. 是否是界面无关紧要。 interface 只是一种声明类型的语法,与标准 type Foo = /* ... */ 相比,它有一些怪癖。现在不用担心这些细节。

    3. 这正是 TypeScript 开发人员决定语法的方式。它与 Java 的语法相匹配,例如,我们有诸如ArrayList&lt;String&gt; 之类的泛型类型和诸如myFunc&lt;SomeType&gt;(a: SomeType) 之类的泛型函数。

    【讨论】:

    • 太棒了,这真是一个绝妙的答案。谢谢!
    • @HarryCramer 乐于助人:)
    【解决方案2】:

    ChangeEvent 就像您的函数可以传递一个类型一样,因此您将一个具体类型传递给它。您也可以将具体类型传递给您的函数 - identity&lt;MyType&gt;(myArg),尽管您可能不需要,因为类型将从您传入的参数中推断出来,因为它们都共享 T

    不同之处在于ChangeEvent 无法从参数推断类型,因为它不是函数,因此您需要显式地给它一个。

    HTMLInputElement 可以是typeinterfaceChangeEvent 将根据 HTML 元素类型而有所不同(从 input 的更改不同于 select 的更改),因此您传入什么类型的元素以获取完整的最终界面。

    【讨论】:

      猜你喜欢
      • 2015-11-27
      • 1970-01-01
      • 2021-11-26
      • 2020-05-26
      • 1970-01-01
      • 2021-10-27
      • 2018-11-17
      • 2021-12-20
      • 2022-08-21
      相关资源
      最近更新 更多