@@ -30,9 +30,9 @@ function getFirst<T>(arr:T[]):T {
30
30
}
31
31
```
32
32
33
- 上面示例中,函数` getFirst() ` 的函数名后面尖括号的部分` <T> ` ,就是类型参数放在一对尖括号 (` <> ` )里面。本例只有一个类型参数` T ` ,可以将其视为类型声明需要的变量,具体的类型由调用时输入的参数类型决定 。
33
+ 上面示例中,函数` getFirst() ` 的函数名后面尖括号的部分` <T> ` ,就是类型参数,参数要放在一对尖括号 (` <> ` )里面。本例只有一个类型参数` T ` ,可以将其理解为类型声明需要的变量,需要在调用时传入具体的参数类型 。
34
34
35
- 参数类型是 ` T[] ` ,返回值类型是` T ` ,就清楚地表示了两者之间的关系。比如,输入的参数类型是` number[] ` ,那么 T 的值就是` number ` ,因此返回值类型也是` number ` 。
35
+ 上例的函数 ` getFirst() ` 的参数类型是 ` T[] ` ,返回值类型是` T ` ,就清楚地表示了两者之间的关系。比如,输入的参数类型是` number[] ` ,那么 T 的值就是` number ` ,因此返回值类型也是` number ` 。
36
36
37
37
函数调用时,需要提供类型参数。
38
38
@@ -72,9 +72,7 @@ comb<number|string>([1, 2], ['a', 'b']) // 正确
72
72
73
73
上面示例中,类型参数是一个联合类型,使得两个参数都符合类型参数,就不报错了。这种情况下,类型参数是不能省略不写的。
74
74
75
- 类型参数的名字,可以随便取,但是必须为合法的标识符。习惯上,类型参数的第一个字符往往采用大写字母。
76
-
77
- 一般会使用` T ` (type 的第一个字母)作为类型参数的名字。如果有多个类型参数,则使用 T 后面的 U、V 等字母命名,各个参数之间使用逗号“,”分隔。
75
+ 类型参数的名字,可以随便取,但是必须为合法的标识符。习惯上,类型参数的第一个字符往往采用大写字母。一般会使用` T ` (type 的第一个字母)作为类型参数的名字。如果有多个类型参数,则使用 T 后面的 U、V 等字母命名,各个参数之间使用逗号(“,”)分隔。
78
76
79
77
下面是多个类型参数的例子。
80
78
@@ -114,16 +112,16 @@ function id<T>(arg:T):T {
114
112
那么对于变量形式定义的函数,泛型有下面两种写法。
115
113
116
114
``` typescript
117
- // 写法一
115
+ // 写法一
118
116
let myId: <T >(arg : T ) => T = id ;
119
117
120
118
// 写法二
121
- let myId: { <T >(arg : T ): T } = id ;
119
+ let myId: { <T >(arg : T ): T } = id ;
122
120
```
123
121
124
122
### 接口的泛型写法
125
123
126
- 泛型函数也可以采用 inteface 的写法 。
124
+ interface 也可以采用泛型的写法 。
127
125
128
126
``` typescript
129
127
interface Box <Type > {
@@ -139,7 +137,7 @@ let box:Box<string>;
139
137
140
138
``` typescript
141
139
interface Comparator <T > {
142
- compareTo(value : T ): number ;
140
+ compareTo(value : T ): number ;
143
141
}
144
142
145
143
class Rectangle implements Comparator <Rectangle > {
@@ -156,19 +154,19 @@ class Rectangle implements Comparator<Rectangle> {
156
154
157
155
``` typescript
158
156
interface Fn {
159
- <Type >(arg : Type ): Type ;
157
+ <Type >(arg : Type ): Type ;
160
158
}
161
159
162
- function id<Type >(arg : Type ): Type {
160
+ function id<Type >(arg : Type ): Type {
163
161
return arg ;
164
162
}
165
-
163
+
166
164
let myId: Fn = id ;
167
165
```
168
166
169
- 上面示例中,类型参数定义在接口内部,所以使用这个接口时(最后一行),不需要给出类型参数的值 。
167
+ 上面示例中,` Fn ` 的类型参数 ` Type ` 的具体类型,需要函数 ` id ` 在使用时提供。所以,最后一行的赋值语句不需要给出 ` Type ` 的具体类型 。
170
168
171
- 除了声明时不需要给出加类型参数,第二种写法还有一个区别。那就是它的类型参数定义在某个方法之上 ,其他属性和方法不能使用该类型参数。前面的第一种写法,类型参数定义在整个接口,接口内部的所有属性和方法都可以使用该类型参数。
169
+ 此外,第二种写法还有一个差异之处。那就是它的类型参数定义在某个方法之中 ,其他属性和方法不能使用该类型参数。前面的第一种写法,类型参数定义在整个接口,接口内部的所有属性和方法都可以使用该类型参数。
172
170
173
171
### 类的泛型写法
174
172
@@ -211,10 +209,10 @@ const b = new Container<number>(0);
211
209
212
210
``` typescript
213
211
class C <NumType > {
214
- value! : NumType ;
215
- add! : (x : NumType , y : NumType ) => NumType ;
212
+ value! : NumType ;
213
+ add! : (x : NumType , y : NumType ) => NumType ;
216
214
}
217
-
215
+
218
216
let foo = new C <number >();
219
217
220
218
foo .value = 0 ;
@@ -228,27 +226,29 @@ foo.add = function (x, y) {
228
226
JavaScript 的类本质上是一个构造函数,因此也可以把泛型类写成构造函数。
229
227
230
228
``` typescript
231
- type Class <T > = new (... args : any []) => T ;
229
+ type MyClass <T > = new (... args : any []) => T ;
232
230
233
231
// 或者
234
- interface Class <T > {
235
- new (... args : any []): T ;
232
+ interface MyClass <T > {
233
+ new (... args : any []): T ;
236
234
}
237
235
238
236
// 用法实例
239
237
function createInstance<T >(
240
- AnyClass : Class <T >,
241
- ... args : any []
238
+ AnyClass : MyClass <T >,
239
+ ... args : any []
242
240
): T {
243
241
return new AnyClass (... args );
244
242
}
245
243
```
246
244
247
- 泛型类描述的是类的实例,不包括静态属性,因为静态属性定义在类的本身。因此,类的静态属性不能引用类型参数。
245
+ 上面示例中,函数` createInstance() ` 的第一个参数` AnyClass ` 是构造函数(也可以是一个类),它的类型是` MyClass<T> ` ,这里的` T ` 是` createInstance() ` 的类型参数,在该函数调用时再指定具体类型。
246
+
247
+ 注意,泛型类描述的是类的实例,不包括静态属性和静态方法,因为这两者定义在类的本身。因此,它们不能引用类型参数。
248
248
249
249
``` typescript
250
250
class C <T > {
251
- static data: T ; // 报错
251
+ static data: T ; // 报错
252
252
constructor (public value : T ) {}
253
253
}
254
254
```
@@ -260,7 +260,7 @@ class C<T> {
260
260
type 命令定义的类型别名,也可以使用泛型。
261
261
262
262
``` typescript
263
- type Nullable <T > = T | undefined | null ;
263
+ type Nullable <T > = T | undefined | null ;
264
264
```
265
265
266
266
上面示例中,` Nullable<T> ` 是一个泛型,只要传入一个类型,就可以得到这个类型与` undefined ` 和` null ` 的一个联合类型。
@@ -271,7 +271,6 @@ type Nullable<T> = T | undefined | null;
271
271
type Container <T > = { value: T };
272
272
273
273
const a: Container <number > = { value: 0 };
274
-
275
274
const b: Container <string > = { value: ' b' };
276
275
```
277
276
@@ -321,7 +320,7 @@ class Generic<T = string> {
321
320
}
322
321
```
323
322
324
- 上面示例中,类` Generic ` 有一个类型参数` T ` ,默认值为` string ` 。这意味着,实例方法 ` add() ` 的参数 ` t ` 的类型,默认是 ` string ` 。
323
+ 上面示例中,类` Generic ` 有一个类型参数` T ` ,默认值为` string ` 。这意味着,属性 ` list ` 默认是一个字符串数组,方法 ` add() ` 的默认参数是一个字符串 。
325
324
326
325
``` typescript
327
326
const g = new Generic ();
@@ -368,12 +367,12 @@ let arr:Array<number> = [1, 2, 3];
368
367
``` typescript
369
368
interface Array <Type > {
370
369
371
- length: number ;
372
-
373
- pop(): Type | undefined ;
374
-
370
+ length: number ;
371
+
372
+ pop(): Type | undefined ;
373
+
375
374
push(... items : Type []): number ;
376
-
375
+
377
376
// ...
378
377
}
379
378
```
@@ -386,13 +385,13 @@ TypeScript 默认还提供一个`ReadonlyArray<T>`接口,表示只读数组。
386
385
387
386
``` typescript
388
387
function doStuff(
389
- values : ReadonlyArray <string >
388
+ values : ReadonlyArray <string >
390
389
) {
391
390
values .push (' hello!' ); // 报错
392
391
}
393
392
```
394
393
395
- 上面示例中,参数` values ` 的类型是` ReadonlyArray<string> ` ,表示不能修改这个数组,所以函数体内部新增数组成员就会报错。因此,如果不希望函数内部改动参数数组,就可以将该参数数组声明为` ReadonlyArray ` 类型。
394
+ 上面示例中,参数` values ` 的类型是` ReadonlyArray<string> ` ,表示不能修改这个数组,所以函数体内部新增数组成员就会报错。因此,如果不希望函数内部改动参数数组,就可以将该参数数组声明为` ReadonlyArray<T> ` 类型。
396
395
397
396
## 类型参数的约束条件
398
397
@@ -407,13 +406,14 @@ function comp<Type>(a:Type, b:Type) {
407
406
}
408
407
```
409
408
410
- 上面示例中,类型参数 Type 有一个隐藏的约束条件:Type 必须是对象,且存在 ` length ` 属性。如果不满足这个条件,就会报错。
409
+ 上面示例中,类型参数 Type 有一个隐藏的约束条件:它必须存在 ` length ` 属性。如果不满足这个条件,就会报错。
411
410
412
- TypeScript 提供了一种语法,允许在类型参数上面写明约束条件,如果不满足条件,编译时就会报错。这样也可以有良好的语义,对类型参数进行了说明 。
411
+ TypeScript 提供了一种语法,允许在类型参数上面写明约束条件,如果不满足条件,编译时就会报错。这样也可以有良好的语义,对类型参数进行说明 。
413
412
414
413
``` typescript
415
414
function comp<T extends { length: number }>(
416
- a : T , b : T
415
+ a : T ,
416
+ b : T
417
417
) {
418
418
if (a .length >= b .length ) {
419
419
return a ;
@@ -470,7 +470,7 @@ type Result = Fn<'hello'> // ["hello", "world"]
470
470
<T extends U , U extends T > // 报错
471
471
` ` `
472
472
473
- 上面示例中, ` T ` 的约束条件不能是 ` T ` 自身,因此多个类型参数也不能互相约束 (即 ` T ` 的约束条件是 ` U ` 、 ` U ` 的约束条件是 ` T ` ),因为互相约束就意味着约束条件就是类型参数自身。
473
+ 上面示例中, ` T ` 的约束条件不能是 ` T ` 自身。同理,多个类型参数也不能互相约束 (即 ` T ` 的约束条件是 ` U ` 、 ` U ` 的约束条件是 ` T ` ),因为互相约束就意味着约束条件就是类型参数自身。
474
474
475
475
## 使用注意点
476
476
@@ -491,7 +491,7 @@ function filter<
491
491
>(
492
492
arr : T [],
493
493
func : Fn
494
- ): T [] {
494
+ ): T [] {
495
495
return arr .filter (func );
496
496
}
497
497
```
@@ -502,7 +502,7 @@ function filter<
502
502
function filter<T >(
503
503
arr : T [],
504
504
func : (arg : T ) => boolean
505
- ): T [] {
505
+ ): T [] {
506
506
return arr .filter (func );
507
507
}
508
508
```
@@ -511,7 +511,7 @@ function filter<T>(
511
511
512
512
** (3)类型参数需要出现两次。**
513
513
514
- 如果类型参数只出现一次 ,那么很可能是不必要的。
514
+ 如果类型参数在定义后只出现一次 ,那么很可能是不必要的。
515
515
516
516
``` typescript
517
517
function greet<Str extends string >(
@@ -535,14 +535,15 @@ function greet(s:string) {
535
535
536
536
** (4)泛型可以嵌套。**
537
537
538
- 类型参数可以是另一个类型参数 。
538
+ 类型参数可以是另一个泛型 。
539
539
540
540
``` typescript
541
541
type OrNull <Type > = Type | null ;
542
-
542
+
543
543
type OneOrMany <Type > = Type | Type [];
544
-
544
+
545
545
type OneOrManyOrNull <Type > = OrNull <OneOrMany <Type >>;
546
546
```
547
547
548
548
上面示例中,最后一行的泛型` OrNull ` 的类型参数,就是另一个泛型` OneOrMany ` 。
549
+
0 commit comments