【问题标题】:In Typescript what does <T> mean?在打字稿中 <T> 是什么意思?
【发布时间】:2018-09-12 07:20:37
【问题描述】:
export declare function createEntityAdapter<T>(options?: {
    selectId?: IdSelector<T>;
    sortComparer?: false | Comparer<T>;
}): EntityAdapter<T>;

谁能解释一下是什么意思?我知道&lt;&gt; 是类型断言,但我不知道'T' 是什么。如果有人能向我解释这个函数在做什么,也会很有帮助。

【问题讨论】:

    标签: typescript


    【解决方案1】:

    谁能给我解释一下&lt;T&gt;是什么意思?

    那是打字稿Generics 声明。

    摘录:

    软件工程的一个主要部分是构建组件,这些组件不仅具有定义明确且一致的 API,而且还可以重用。能够处理当今数据和未来数据的组件将为您提供构建大型软件系统的最灵活功能。

    在 C# 和 Java 等语言中,工具箱中用于创建可重用组件的主要工具之一是泛型,也就是说,能够创建一个可以在多种类型而不是单一类型上工作的组件。这允许用户使用这些组件并使用他们自己的类型。

    你提到:

    我不知道“T”是什么。

    'T' 将是在运行时而不是 compile 时声明的类型。 T 变量可以是任何未声明的变量(我找不到参考,但我会假设任何可用于变量名称的有效字符集)。同样在 中,如果T 表示的类型不是值类型而是更复杂的类型(类)或接口,则可以将其命名/声明为TVehicleTAnimal 以帮助表示一个有效类型未来的程序员(并且可以被认为是最佳实践,因为 T 并不直观)。我更喜欢TSomething,因为我知道大写的 T 表示泛型。 WSometingASomething 也是有效的,但我只是不喜欢它。 (例如,Microsoft 的 API 几乎总是 TContextTEntity)。

    如果有人可以向我解释这个函数在做什么,那也会很有帮助。

    这个函数没有任何事情。这更像是声明一种可以具有多个运行时类型值的函数。我将直接从上面的链接中摘录一段,而不是解释这一点。

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

    可以这样使用:

    // type of output will be 'string'
    let output = identity<string>("myString");  
    

    // type of output will be 'string', the compiler will figure out `T`
    // based on the value passed in
    let output = identity("myString");  
    

    // type of output will be 'number'
    let output = identity(8675309);  
    

    这可能会引发问题:

    为什么要使用泛型

    Javascript 有数组,但是当你从数组中检索一个值时,它实际上可以是任何东西(打字稿:any)。使用 typescript,您可以通过以下方式声明它们来获得类型安全:

     // Array<T>
     let list: number[] = [1, 2, 3];
     // or 
     let list: Array<number> = [1, 2, 3];
    

    现在数组中的每个值都有一个类型。如果您尝试将字符串放入此数组,Typescript 将引发编译时错误。当您检索一个值时,您会获得类型安全和智能感知(取决于您的编辑器):

    class Person {
      FirstName: string;
    }
    
    let people: Array<Person> = [];
    people.push({ FirstName: "John" } as Person);
    
    let john = people.pop();  
    // john is of type Person, the typescript compiler knows this
    // because we've declared the people variable as an array of Person
    
    console.log(john.FirstName);  
    

    声明类型化的通用约束。 Open - Closed Principle 的一个很好的例子。

    在面向对象编程中,开闭原则指出“软件实体(类、模块、函数等)应该对扩展开放,对修改关闭”;[1] 即这样的实体可以允许在不修改其源代码的情况下扩展其行为。

    在以下示例中,任何人都可以扩展 Human 或 Cheetah,甚至创建自己的派生类型,并且 Logger 功能将继续工作而无需任何修改。

    interface IAnimal {
      LegCount: number;
    }
    
    class Cheetah 
      implements IAnimal {
      LegCount: number = 4;
    }
    
    class Human
      implements IAnimal {
      LegCount: number = 2;
    }
    
    public class Logger<TAnimal extends IAnimal> {
      public Log(animal: TAnimal) {
        console.log(animal.LegCount);
      }
    }
     
    var logger = new Logger();
    var human = new Human();
    logger.Log(human);      
    

    Working Example

    在前面的示例中,我使用Generic ConstraintTAnimal 类型限制为程序员可以用来创建Logger 实例的类型,这些类型派生自接口IAnimal。这允许编译器验证 Logger 类是否始终假定该类型具有属性 LegCount

    您可以向我解释为什么他们在 Typescript 文档中放置 而不是放置更具描述性的内容,例如 。对我来说没有什么和 是一样的。现在是不是每个人都像傻瓜一样使用 ,还是我错过了什么?

    这些都是下面的假设。我不知道设计打字稿通用系统的团队和编写文档的团队。

    在泛型的根级别是能够将T 用作任何可能的类型(不要与打字稿any 混淆)。意思是Array&lt;T&gt;接口(因为没有更好的词),当我们创建一个具体类型时,我们用声明的类型替换T

    Array<number>
    

    那么对于 界面 Array&lt;T&gt; 有什么比 T 更有意义?我不知道。我知道T 必须是一个类型(数字、字符串等),所以使用T 是有意义的,因为它是单词Type 的第一个字母。我认为如果 typeType 被保留或限制,Array&lt;Type&gt; 将非常令人困惑和/或什至可能无效(目前type 在某些情况下具有特殊含义,所以这也是一个糟糕的选择)所以避免这些是一个不错的选择。其他语言(C-sharpJava)也选择使用T,因此在语言之间切换并能够使用相同的术语是有利的。

    另一方面,以下是什么意思?

    Array<Identity>
    

    这里的Identity 是什么?没有任何限制可以帮助其他开发人员或未来的开发人员知道它是什么。在我看来,它是一个我必须显式实现的特定类型数组,这意味着我不能选择泛型类型。

    interface Foo1 {
      bars: Array<Identity>;
    }
    

    在前面的示例中,我(可能还有大多数开发人员)会假设 Identity 是一个现有类型,我无法更改它。

    interface Foo2<T> {
      bars: Array<T>;
    }
    

    有了Foo2,我知道我必须选择一种类型。

    interface Foo3<Identity> {
      bars: Array<Identity>;
    }
    

    Foo3 只是令人困惑。

    interface Foo4<TIdentity> {
      bars: Array<TIdentity>;
    }
    

    现在有了Foo4,我更有信心必须选择类型,但我仍然有点困惑为什么TIdentity。显然,在某些情况下,类型更明确,这是有道理的。

    编辑: 自 2021 年 3 月起,Typescript Documentation 已更新为在文档中弃用 &lt;T&gt;,取而代之的是 &lt;Type&gt;

    既然如此,我个人发现以下内容令人困惑,并强烈建议避免使用Type,原因有两个。

    interface Lengthwise {
      length: number;
    }
    
    function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
      console.log(arg.length); 
      return arg;
    }
    

    在使用约束时避免将类型命名为Type 的第一个原因是它在方法中和多个方法中都不能很好地读取。想象一下,日志记录方法比 2 行长得多,突然间,你读到的所有东西都是 Type 这个词。

    interface Lengthwise {
      length: number;
    }
    
    interface Widthwise {
      width: number;
    }
    
    function loggingLength<Type extends Lengthwise>(arg: Type): Type {
      console.log(arg.length);
      return arg;
    }
    
    function loggingWidth<Type extends Widthwise >(arg: Type): Type {
      console.log(arg.width);
      return arg;
    }
    
    

    我认为以下内容更具可读性:

    interface Lengthwise {
      length: number;
    }
    
    interface Widthwise {
      width: number;
    }
    
    function loggingLength<TLengthwise extends Lengthwise>(arg: TLengthwise): TLengthwise {
      console.log(arg.length);
      return arg;
    }
    
    function loggingWidth<TWidthwise extends Lengthwise>(arg: TWidthwise ): TWidthwise {
      console.log(arg.length);
      return arg;
    }
    
    

    其次,很可能有 1 个以上的泛型,这使得以下内容无效(希望是出于显而易见的原因)。

    interface Lengthwise {
      length: number;
    }
    
    interface Widthwise {
      width: number;
    }
    
    function loggingLength<Type extends Lengthwise, Type extends Widthwise>
    (arg1: Type, arg2: Type) {
      console.log(arg1.length);
      console.log(arg2.width);
    }
    
    

    虽然文档使用Type 来展示简单和平凡的示例,但我强烈建议不要在任何实际代码中使用Type

    【讨论】:

    • 菲利普斯 我也见过 。这些是什么意思,或者我可以在尖括号内使用任何字符吗?
    • @CharlesRobertson 更新了答案,感谢这将对未来的读者有所帮助,很好的问题。
    • 感谢@ErikPhilips,您可以向我解释为什么他们在 Typescript 文档中添加了 &lt;T&gt; 而不是像 &lt;Identity&gt; 这样更具描述性的内容。没有什么和&lt;T&gt; 对我来说是一样的。现在每个人都像傻瓜一样使用&lt;T&gt;,还是我错过了什么?
    • @jcarlosweb 更新了我的回答,希望能回答你的问题。
    • 很好的答案,谢谢。
    【解决方案2】:

    您提供的示例是一个带有泛型参数的函数。 T(不一定是T。你可以称它为G。)称为通用模板,其中T 的实际类型在运行时被替换。

    想象一下 EntityAdapter 有以下实现:

    interface EntityAdapter<T> {
       save(entity: T);
    }
    

    下面的代码返回一个EntityAdapter,它的内容是anyany 可以是数字、字符串、对象或任何东西。

    let adapter1 = createEntityAdapter<any>(<parameters here>)
    

    以下代码返回一个EntityAdapter,其内容为Car

    let adapter2 = createEntityAdapter<Car>(<parameters here>)
    

    基本上Carany 更具体,因此您可以获得额外的类型安全性。那么这有什么帮助呢?

    简而言之,泛型模板可以为您提供额外的类型安全性。例如,

    adapter1.save('I am string') // this works because `T` is `any`
    adapter1.save(new Car()) // this also works because `T` is `any`
    
    adapter2.save('I am string') // this wont works  because `T` is `Car`, typescript compiler will complains
    adapter2.save(new Car()) //this works because `T` is `Car`
    

    希望这会有所帮助。

    【讨论】:

    • 我喜欢你解释 T 可以是任何字符或字符集的方式。我经常看到 并想知道它们是否有特殊含义。我猜这意味着“不”?
    • @CharlesRobertson 是的,你是对的。就打字稿而言,它们没有任何特殊含义。
    • 为了清楚起见,如果我理解这一点,T 只是以后类型使用的占位符。对吗?
    【解决方案3】:

    遗憾的是,我没有为@Erik Philips 的顶级答案下的出色讨论做出贡献所需的“50 个声望点”,但值得注意的是,截至 2021 年 3 月,Typescript 文档已已更新以弃用在文档中使用 &lt;T&gt;,而不是 &lt;Type&gt;Link

    就我而言,这很令人困惑,因为我找不到演示T 用法的旧文档。但是,Erik Philips 的回答帮助我了解,这以前是旧的(现已弃用)Typescript 文档中展示的约定。

    更新当前答案以包含此信息可能会有所帮助。

    【讨论】:

    • 你已经接近了! :D 一个接受的答案只是最能代表 OP 要求的答案的答案。确实没有理由更新它,因为多个答案是一件好事。
    • 虽然Type 的使用在文档中,但我添加了值得的理由不使用它
    • 确实,我觉得有趣的是,微软选择将Type 设为标准。
    【解决方案4】:

    它被称为泛型,帮助我们编写安全而灵活的函数。它为类型提供变量。简而言之,您可以创建适用于不同类型的函数、接口或类,而无需预先指定类型。 下面是一些演示代码来解释我们为什么使用它以及如何使用它。

    // any is too general ,and it loses the meaning of using typescript
    
    function insertAtBegining(array:any[],value:any) {
        let newArr = [value, ...array];
        return newArr;
    }
    
    // if we specify the type number , this function will lost it's utility 
    
    function insertAtBegining(array:number[],value:number) {
        let newArr = [value, ...array];
        return newArr;
    }
    
    // if we specify the type string , this function will lost it's utility 
    
    function insertAtBegining(array:string[],value:string) {
        let newArr = [value, ...array];
        return newArr;
    }
    
    const demoArr = [1, 2, 3];
    
    const updatedArr = insertAtBegining(demoArr, 4);
    // more generic functions
    
    function insertAtBegining<T>(array:T[],value:T) {
        let newArr = [value, ...array];
        return newArr;
    }

    【讨论】:

      猜你喜欢
      • 2021-04-07
      • 2019-11-15
      • 2021-04-24
      • 2021-04-21
      • 2021-01-02
      • 2021-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多