Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f3fe894

Browse files
committedJul 21, 2023
docs: finish chapter generics
1 parent 56c894d commit f3fe894

File tree

2 files changed

+46
-44
lines changed

2 files changed

+46
-44
lines changed
 

‎chapters.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
- object.md: 对象
1010
- interface.md: interface
1111
- class.md:
12+
- generics.md: 泛型

‎docs/generics.md

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ function getFirst<T>(arr:T[]):T {
3030
}
3131
```
3232

33-
上面示例中,函数`getFirst()`的函数名后面尖括号的部分`<T>`就是类型参数放在一对尖括号`<>`)里面。本例只有一个类型参数`T`可以将其视为类型声明需要的变量,具体的类型由调用时输入的参数类型决定
33+
上面示例中,函数`getFirst()`的函数名后面尖括号的部分`<T>`就是类型参数,参数要放在一对尖括号`<>`)里面。本例只有一个类型参数`T`可以将其理解为类型声明需要的变量,需要在调用时传入具体的参数类型
3434

35-
参数类型是`T[]`,返回值类型是`T`,就清楚地表示了两者之间的关系。比如,输入的参数类型是`number[]`,那么 T 的值就是`number`,因此返回值类型也是`number`
35+
上例的函数`getFirst()`的参数类型是`T[]`,返回值类型是`T`,就清楚地表示了两者之间的关系。比如,输入的参数类型是`number[]`,那么 T 的值就是`number`,因此返回值类型也是`number`
3636

3737
函数调用时,需要提供类型参数。
3838

@@ -72,9 +72,7 @@ comb<number|string>([1, 2], ['a', 'b']) // 正确
7272

7373
上面示例中,类型参数是一个联合类型,使得两个参数都符合类型参数,就不报错了。这种情况下,类型参数是不能省略不写的。
7474

75-
类型参数的名字,可以随便取,但是必须为合法的标识符。习惯上,类型参数的第一个字符往往采用大写字母。
76-
77-
一般会使用`T`(type 的第一个字母)作为类型参数的名字。如果有多个类型参数,则使用 T 后面的 U、V 等字母命名,各个参数之间使用逗号“,”分隔。
75+
类型参数的名字,可以随便取,但是必须为合法的标识符。习惯上,类型参数的第一个字符往往采用大写字母。一般会使用`T`(type 的第一个字母)作为类型参数的名字。如果有多个类型参数,则使用 T 后面的 U、V 等字母命名,各个参数之间使用逗号(“,”)分隔。
7876

7977
下面是多个类型参数的例子。
8078

@@ -114,16 +112,16 @@ function id<T>(arg:T):T {
114112
那么对于变量形式定义的函数,泛型有下面两种写法。
115113

116114
```typescript
117-
// 写法一
115+
// 写法一
118116
let myId:<T>(arg:T) => T = id;
119117

120118
// 写法二
121-
let myId:{ <T>(arg:T):T } = id;
119+
let myId:{ <T>(arg:T): T } = id;
122120
```
123121

124122
### 接口的泛型写法
125123

126-
泛型函数也可以采用 inteface 的写法
124+
interface 也可以采用泛型的写法
127125

128126
```typescript
129127
interface Box<Type> {
@@ -139,7 +137,7 @@ let box:Box<string>;
139137

140138
```typescript
141139
interface Comparator<T> {
142-
compareTo(value:T):number;
140+
compareTo(value:T): number;
143141
}
144142

145143
class Rectangle implements Comparator<Rectangle> {
@@ -156,19 +154,19 @@ class Rectangle implements Comparator<Rectangle> {
156154

157155
```typescript
158156
interface Fn {
159-
<Type>(arg:Type):Type;
157+
<Type>(arg:Type): Type;
160158
}
161159

162-
function id<Type>(arg:Type):Type {
160+
function id<Type>(arg:Type): Type {
163161
return arg;
164162
}
165-
163+
166164
let myId:Fn = id;
167165
```
168166

169-
上面示例中,类型参数定义在接口内部,所以使用这个接口时(最后一行),不需要给出类型参数的值
167+
上面示例中,`Fn`的类型参数`Type`的具体类型,需要函数`id`在使用时提供。所以,最后一行的赋值语句不需要给出`Type`的具体类型
170168

171-
除了声明时不需要给出加类型参数,第二种写法还有一个区别。那就是它的类型参数定义在某个方法之上,其他属性和方法不能使用该类型参数。前面的第一种写法,类型参数定义在整个接口,接口内部的所有属性和方法都可以使用该类型参数。
169+
此外,第二种写法还有一个差异之处。那就是它的类型参数定义在某个方法之中,其他属性和方法不能使用该类型参数。前面的第一种写法,类型参数定义在整个接口,接口内部的所有属性和方法都可以使用该类型参数。
172170

173171
### 类的泛型写法
174172

@@ -211,10 +209,10 @@ const b = new Container<number>(0);
211209

212210
```typescript
213211
class C<NumType> {
214-
value!:NumType;
215-
add!:(x: NumType, y: NumType) => NumType;
212+
value!: NumType;
213+
add!: (x: NumType, y: NumType) => NumType;
216214
}
217-
215+
218216
let foo = new C<number>();
219217

220218
foo.value = 0;
@@ -228,27 +226,29 @@ foo.add = function (x, y) {
228226
JavaScript 的类本质上是一个构造函数,因此也可以把泛型类写成构造函数。
229227

230228
```typescript
231-
type Class<T> = new (...args: any[]) => T;
229+
type MyClass<T> = new (...args: any[]) => T;
232230

233231
// 或者
234-
interface Class<T> {
235-
new(...args: any[]):T;
232+
interface MyClass<T> {
233+
new(...args: any[]): T;
236234
}
237235

238236
// 用法实例
239237
function createInstance<T>(
240-
AnyClass:Class<T>,
241-
...args:any[]
238+
AnyClass: MyClass<T>,
239+
...args: any[]
242240
):T {
243241
return new AnyClass(...args);
244242
}
245243
```
246244

247-
泛型类描述的是类的实例,不包括静态属性,因为静态属性定义在类的本身。因此,类的静态属性不能引用类型参数。
245+
上面示例中,函数`createInstance()`的第一个参数`AnyClass`是构造函数(也可以是一个类),它的类型是`MyClass<T>`,这里的`T``createInstance()`的类型参数,在该函数调用时再指定具体类型。
246+
247+
注意,泛型类描述的是类的实例,不包括静态属性和静态方法,因为这两者定义在类的本身。因此,它们不能引用类型参数。
248248

249249
```typescript
250250
class C<T> {
251-
static data:T; // 报错
251+
static data: T; // 报错
252252
constructor(public value:T) {}
253253
}
254254
```
@@ -260,7 +260,7 @@ class C<T> {
260260
type 命令定义的类型别名,也可以使用泛型。
261261

262262
```typescript
263-
type Nullable<T> = T | undefined | null;
263+
type Nullable<T> = T | undefined | null;
264264
```
265265

266266
上面示例中,`Nullable<T>`是一个泛型,只要传入一个类型,就可以得到这个类型与`undefined``null`的一个联合类型。
@@ -271,7 +271,6 @@ type Nullable<T> = T | undefined | null;
271271
type Container<T> = { value: T };
272272

273273
const a: Container<number> = { value: 0 };
274-
275274
const b: Container<string> = { value: 'b' };
276275
```
277276

@@ -321,7 +320,7 @@ class Generic<T = string> {
321320
}
322321
```
323322

324-
上面示例中,类`Generic`有一个类型参数`T`,默认值为`string`。这意味着,实例方法`add()`的参数`t`的类型,默认是`string`
323+
上面示例中,类`Generic`有一个类型参数`T`,默认值为`string`。这意味着,属性`list`默认是一个字符串数组,方法`add()`的默认参数是一个字符串
325324

326325
```typescript
327326
const g = new Generic();
@@ -368,12 +367,12 @@ let arr:Array<number> = [1, 2, 3];
368367
```typescript
369368
interface Array<Type> {
370369

371-
length:number;
372-
373-
pop():Type | undefined;
374-
370+
length: number;
371+
372+
pop(): Type|undefined;
373+
375374
push(...items:Type[]): number;
376-
375+
377376
// ...
378377
}
379378
```
@@ -386,13 +385,13 @@ TypeScript 默认还提供一个`ReadonlyArray<T>`接口,表示只读数组。
386385

387386
```typescript
388387
function doStuff(
389-
values: ReadonlyArray<string>
388+
values:ReadonlyArray<string>
390389
) {
391390
values.push('hello!'); // 报错
392391
}
393392
```
394393

395-
上面示例中,参数`values`的类型是`ReadonlyArray<string>`,表示不能修改这个数组,所以函数体内部新增数组成员就会报错。因此,如果不希望函数内部改动参数数组,就可以将该参数数组声明为`ReadonlyArray`类型。
394+
上面示例中,参数`values`的类型是`ReadonlyArray<string>`,表示不能修改这个数组,所以函数体内部新增数组成员就会报错。因此,如果不希望函数内部改动参数数组,就可以将该参数数组声明为`ReadonlyArray<T>`类型。
396395

397396
## 类型参数的约束条件
398397

@@ -407,13 +406,14 @@ function comp<Type>(a:Type, b:Type) {
407406
}
408407
```
409408

410-
上面示例中,类型参数 Type 有一个隐藏的约束条件:Type 必须是对象,且存在`length`属性。如果不满足这个条件,就会报错。
409+
上面示例中,类型参数 Type 有一个隐藏的约束条件:它必须存在`length`属性。如果不满足这个条件,就会报错。
411410

412-
TypeScript 提供了一种语法,允许在类型参数上面写明约束条件,如果不满足条件,编译时就会报错。这样也可以有良好的语义,对类型参数进行了说明
411+
TypeScript 提供了一种语法,允许在类型参数上面写明约束条件,如果不满足条件,编译时就会报错。这样也可以有良好的语义,对类型参数进行说明
413412

414413
```typescript
415414
function comp<T extends { length: number }>(
416-
a:T, b:T
415+
a: T,
416+
b: T
417417
) {
418418
if (a.length >= b.length) {
419419
return a;
@@ -470,7 +470,7 @@ type Result = Fn<'hello'> // ["hello", "world"]
470470
<T extends U, U extends T> // 报错
471471
```
472472
473-
上面示例中,`T`的约束条件不能是`T`自身,因此多个类型参数也不能互相约束(即`T`的约束条件是`U``U`的约束条件是`T`),因为互相约束就意味着约束条件就是类型参数自身。
473+
上面示例中,`T`的约束条件不能是`T`自身。同理,多个类型参数也不能互相约束(即`T`的约束条件是`U``U`的约束条件是`T`),因为互相约束就意味着约束条件就是类型参数自身。
474474
475475
## 使用注意点
476476
@@ -491,7 +491,7 @@ function filter<
491491
>(
492492
arr:T[],
493493
func:Fn
494-
):T[] {
494+
): T[] {
495495
return arr.filter(func);
496496
}
497497
```
@@ -502,7 +502,7 @@ function filter<
502502
function filter<T>(
503503
arr:T[],
504504
func:(arg:T) => boolean
505-
):T[] {
505+
): T[] {
506506
return arr.filter(func);
507507
}
508508
```
@@ -511,7 +511,7 @@ function filter<T>(
511511

512512
**(3)类型参数需要出现两次。**
513513

514-
如果类型参数只出现一次,那么很可能是不必要的。
514+
如果类型参数在定义后只出现一次,那么很可能是不必要的。
515515

516516
```typescript
517517
function greet<Str extends string>(
@@ -535,14 +535,15 @@ function greet(s:string) {
535535

536536
**(4)泛型可以嵌套。**
537537

538-
类型参数可以是另一个类型参数
538+
类型参数可以是另一个泛型
539539

540540
```typescript
541541
type OrNull<Type> = Type|null;
542-
542+
543543
type OneOrMany<Type> = Type|Type[];
544-
544+
545545
type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;
546546
```
547547

548548
上面示例中,最后一行的泛型`OrNull`的类型参数,就是另一个泛型`OneOrMany`
549+

0 commit comments

Comments
 (0)
Please sign in to comment.