我想知道为什么尖括号是TypeScript语言的一部分,尖括号在程序上的作用是什么,尖括号对其中的代码有什么影响。
例如:这里添加尖括号的目的是什么?我应该如何解释它们?
getContent<K extends keyof ContentMap>(content: K, conf?: ContentMap[K]["conf"]): Promise<Readonly<ContentMap[K]["content"]>>;
getContent<K extends keyof ContentMap>(content: K, conf?: ContentMap[K]["conf"]): Promise<Readonly<ContentMap[K]["content"]>>;
<>
时,实际上你写的是Anders代码,而不是Typescript代码。这段代码可以被显式地调用(当你写像MyType<T>
这样的东西时),也可以通过类型推断在幕后隐蔽地调用。function pair (x, y) {
return [x, y]
}
这是Anders函数,它接受两种类型并根据它们返回另一种类型:
type Pair<U, V> = [U, V]
pair
两个值,您将得到这两个值的数组。Pair
一个number
(不是任意数字,而是"number"类型)和一个string
,您将得到[number, string]
,这是所有可能的number,string
数组的类型,例如[1, "hi"]
或[3.14, "hey"]
。如果您给它string
和boolean
,您将得到所有类似["hi", true]
,["blah", false]
的数组类型。built-in types, like number
, string
, any
, {}
. These are similar to Typescript built-in objects like "Number" or "String".
literals, like "foo"
. These are similar to literals in Typescript, but while in TS "foo"
means a specific string, e.g. a sequence of characters f, o, o
, in Anders it means a type, namely, "the type of all strings that are foo", which, obviously, has only one possible member, "foo"
.
unions, similar to arrays in TS: A|B|C
.
structures, similar to objects in TS. In TS, an object maps strings to values. In Anders, a structure (aka "mapped type"), maps types to other types. The index operator S[B]
returns the type to which the structure S
maps B
{foo: string; bar:number}["foo"]` ====> string
operators, e.g. the unary keyof
operator takes a type A
and returns the type of all possible keys of A
, that is, a union (array) TypeOfKey1 | TypeOfKey2 | ...
keyof {foo:string, bar:number} =====> "foo"|"bar"
comparisons, like a > b
in TS. Anders only has one form of comparison, A extends B
, which means that A
is a subset of B
, that is, all possible values of the type A
are also values of B
, but not necessarily the other way around.
"foo" extends string =====> ok
"foo" extends "foo"|"bar" =====> ok
"blag" extends "foo"|"bar" =====> not ok
conditionals: comparison ? Type1 : Type2
loops, like {[A in SomeUnion]: T}
. This creates a structure, whose keys are the union members and values are of type T
{[A in "foo"|"bar"]: number} =====> {foo:number, bar:number}
function calls, which are SomeOtherTypeDeclaration<Type1, Type2, ...>
finally, Anders also have type checks for input parameters, similar to function foo(x:number)
in Typescript. In Anders, a type check is a comparison, that is, 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
和Y
的参数和返回值为Z
类型的函数的类型。
让我们手动使用不同的类型"调用"此函数并查看发生了什么。
getContent<number>
。首先,Anders检查参数的类型。我们的类型检查为extends keyof ContentMap
。回想一下,keyof ContentMap
返回ContentMap
键的数组,即"foo"|"bar"
,其中"foo"
和"bar"
是类型而不仅仅是字符串。然后,我们的参数number
与"foo"|"bar"
进行比较。显然,number
不是这种类型的子集,因此类型检查失败,我们会得到一个错误。
getContent<"foo">
。类型检查成功(因为"foo"
是"foo"|"bar"
的子集),然后我们可以继续。我们的任务是基于"foo"
构建函数类型。第一个参数的类型为K
,与参数相同,所以它变成了"foo"
。第二个参数应用索引运算符两次:首先,我们评估ContentMap["foo"]
,它给出{conf: A, content: B}
,然后我们应用["conf"]
,它给出A
。以类似的方式,我们为返回类型获得B
。最后,我们调用内置的Anders函数Readonly
并得到另一种类型,让我们称之为ReadonlyB
。因此,我们得到的是函数类型(content: "foo", conf: A) => ReadonlyB
,这就是我们的Anders函数返回的内容。
getContent<"bar">
... 留作练习。
现在,当您编写这个时,会发生什么?
let something = getContent('foo', {...})
getContent
相关的 Anders 代码,并评估该代码,将 "foo"
作为参数传递。如上所示,返回类型将是 ("foo", A) => ReadonlyB
。然后,上述行将与此类型进行检查,如果不匹配,则失败,这基本上就是整个过程的全部内容。正如@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
在TypeScript中使用的尖括号被称为泛型。这里有一个关于泛型的优秀视频。