在Typescript中的函数声明中,尖括号“ <>”是什么意思?

时间:2019-09-29 08:00:46

标签: typescript

我知道What does enclosing a class in angle brackets “<>” mean in TypeScript?中有一个几乎相似的问题

但是当我发现它以复杂的方式(对于新手)在接口内声明函数时,仍然感到困惑。

getContent<K extends keyof ContentMap>(content: K, conf?: ContentMap[K]["conf"]): Promise<Readonly<ContentMap[K]["content"]>>;

尖括号使用了很多次(甚至递归)。我怎么读?

2 个答案:

答案 0 :(得分:3)

学习打字稿时,实际上不是学习一种语言,而是学习两种语言。第一语言是Typescript专有语言,它是带有类型注释和一些扩展名的Javascript,例如“枚举”或“公共/私有”类成员。第二种语言是类型的语言。它没有正式名称,我们以发明人Anders Hejlsberg的名字命名为Anders。

Anders的目的是为程序生成动态类型。虽然Typescript可以处理字符串,数字,对象等值,但是Anders仅处理一种数据:类型本身。安德斯的价值观是类型。 Anders中的函数接受一个或多个类型参数,然后返回另一种类型。

每次在程序中使用<>时,实际上是在编写Anders代码,而不是Typescript代码。可以通过类型推断来显式调用此代码(在编写MyType<T>之类的代码时),也可以在后台进行调用。

例如,这是一个Typescript函数,该函数接受两个值并基于它们返回另一个值:

function pair (x, y) {
    return [x, y]
}

这是一个Anders函数,它接受两种类型并基于它们返回另一种类型:

type Pair<U, V> = [U, V]

在Typescript中,如果给pair两个值,则将得到这两个值的数组。

在安德斯(Anders)中,如果您输入Pair number(不是任何数字,即“数字”类型)和string,您将得到[number, string],该数字是所有可能的number,string数组的类型,例如[1, "hi"][3.14, "hey"]。如果您给它stringboolean,则将获得所有数组的类型,例如["hi", true]["blah", false]

与其他语言一样,安德斯(Anders)提供了基本的编程构造(概括地说,它们都是类型或作用于类型,而不是值):

  • 内置类型,例如numberstringany{}。这些类似于Typescript内置对象,例如“ Number”或“ String”。

  • 文字,例如"foo"。这些类似于Typescript中的文字,但是在TS "foo"中表示特定的字符串,例如在Anders中,字符序列f, o, o的意思是一种类型,即“所有为foo的字符串的类型”,很显然,它只有一个可能的成员"foo"

  • 联合,类似于TS中的数组:A|B|C

  • 结构,类似于TS中的对象。在TS中,对象将字符串映射到值。在Anders中,结构(也称为“映射类型”)将类型映射为其他类型。索引运算符S[B]返回结构S映射到的类型B

        {foo: string; bar:number}["foo"]` ====> string
    
  • 个运算符,例如一元keyof运算符采用类型A并返回A的所有可能键的类型,即,联合(数组)TypeOfKey1 | TypeOfKey2 | ...

        keyof {foo:string, bar:number} =====> "foo"|"bar"
    
  • 比较,例如TS中的a > b。 Anders仅具有一种比较形式,即A extends B,这意味着AB的子集,也就是说,类型A的所有可能值也是B,但不一定相反。

        "foo" extends string =====> ok
        "foo" extends "foo"|"bar" =====> ok
        "blag" extends "foo"|"bar" =====> not ok
    
  • 条件词:comparison ? Type1 : Type2

  • 循环,如{[A in SomeUnion]: T}。这将创建一个结构,其键为联合成员,值的类型为T

        {[A in "foo"|"bar"]: number} =====> {foo:number, bar:number}
    
  • 函数调用,即SomeOtherTypeDeclaration<Type1, Type2, ...>

  • 最后,Anders还对输入参数进行类型检查,类似于Typescript中的function foo(x:number)。在Anders中,类型检查是一种比较,即A extends B

现在,回到您的示例(为清楚起见,对其进行了简化)。

interface A {}
interface B {}
interface C {}
interface D {}

type ContentMap = {
  foo: {
      conf: A
      content: B
  },
  bar: {
      conf: C
      content: D
  }
}

function getContent<K extends keyof ContentMap>
  ( content: K,
    conf?: ContentMap[K]["conf"]
  ): Readonly<ContentMap[K]["content"]> {
      ...
  }

getContent是Anders函数,它接受类型K,并返回另一个类型(X, Y) => Z,它是所有具有两个参数类型X和{{1 }},返回值的类型为Y

让我们以不同类型手动“调用”此功能,然后看看会发生什么。

  1. Z。首先,Anders检查参数的类型。我们的类型检查是getContent<number>。我们记得,extends keyof ContentMap返回了keyof ContentMap的键数组,也就是ContentMap,其中"foo"|"bar""foo"都是类型,而不仅仅是字符串。然后,将我们的参数"bar"number进行比较。显然,"foo"|"bar"不是该类型的子集,因此类型检查失败,并且会出现错误。

  2. number。类型检查成功(由于getContent<"foo"> "foo"的子集),我们可以继续进行。我们的任务是基于"foo"|"bar"构造函数类型。第一个参数的类型为"foo",与参数相同,因此它只是K。第二个参数两次应用了索引运算符:首先,我们评估"foo",这给了我们ContentMap["foo"],然后我们应用了{conf: A, content: B},这给了我们["conf"]。以类似的方式,我们获得A作为返回类型。最后,我们调用内置的Anders函数B并返回另一种类型,我们将其称为Readonly,因此,我们得到的是函数类型ReadonlyB,这是我们的Anders函数返回什么。

  3. (content: "foo", conf: A) => ReadonlyB ...作为练习。

现在,当你写这篇文章时会发生什么?

getContent<"bar">

编译器会发现您有一些与let something = getContent('foo', {...}) 相关的Anders代码,并通过传递getContent作为参数来评估该代码。如上所示,返回类型将为"foo"。然后,对上述类型的行进行检查,如果不匹配则失败,这基本上就是全部内容。

希望这对您有帮助...

答案 1 :(得分:1)

如@axiac所述,这与泛型有关。

阅读方法是思考类型

示例:

// generic class that deals with type T
class List<T> {}

// usage
const list1 = new List<string>() // list of type string
const list2 = new List<number>() // list of type number
const list3 = new List<any>()    // list of type any