@@ -31,20 +31,20 @@ interface T {
31
31
type KeyT = keyof T ; // 0 | 'a' | 'b'
32
32
```
33
33
34
- 由于 JavaScript 对象的键名只有三种类型,所以对于任意键名的联合类型就是 ` string|number|symbol ` 。
34
+ 由于 JavaScript 对象的键名只有三种类型,所以对于任意对象的键名的联合类型就是 ` string|number|symbol ` 。
35
35
36
36
``` typescript
37
37
// string | number | symbol
38
38
type KeyT = keyof any ;
39
39
```
40
40
41
- 对于上面三种类型以外的类型使用 keyof 运算符,返回` never ` 类型,表示不可能有这样类型的键名。
41
+ 对于没有自定义键名的类型使用 keyof 运算符,返回` never ` 类型,表示不可能有这样类型的键名。
42
42
43
43
``` typescript
44
44
type KeyT = keyof object ; // never
45
45
```
46
46
47
- 上面示例中,由于不可能有 ` object ` 类型的键名 ,所以` keyof object ` 返回` never ` 类型。
47
+ 上面示例中,由于 ` object ` 类型没有自身的属性,也就没有键名 ,所以` keyof object ` 返回` never ` 类型。
48
48
49
49
由于 keyof 返回的类型是` string|number|symbol ` ,如果有些场合只需要其中的一种类型,那么可以采用交叉类型的写法。
50
50
@@ -54,9 +54,7 @@ type Capital<T extends string> = Capitalize<T>;
54
54
type MyKeys <Obj extends object > = Capital <keyof Obj >; // 报错
55
55
```
56
56
57
- 上面示例中,类型` Capital ` 只接受字符串作为类型参数,传入` keyof Obj ` 会报错,原因是这时的类型参数是` string|number|symbol ` ,跟字符串不兼容。
58
-
59
- 采用下面的交叉类型写法,就不会报错。
57
+ 上面示例中,类型` Capital ` 只接受字符串作为类型参数,传入` keyof Obj ` 会报错,原因是这时的类型参数是` string|number|symbol ` ,跟字符串不兼容。采用下面的交叉类型写法,就不会报错。
60
58
61
59
``` typescript
62
60
type MyKeys <Obj extends object > = Capital <string & keyof Obj >;
@@ -89,21 +87,21 @@ type KeyT = keyof T;
89
87
如果 keyof 运算符用于数组或元组类型,得到的结果可能出人意料。
90
88
91
89
``` typescript
90
+ type Result = keyof [' a' , ' b' , ' c' ];
92
91
// 返回 number | "0" | "1" | "2"
93
92
// | "length" | "pop" | "push" | ···
94
- type Result = keyof [' a' , ' b' , ' c' ];
95
93
```
96
94
97
- 上面示例中,keyof 会返回数组的所有属性名,包括字符串属性名和继承的属性名 。
95
+ 上面示例中,keyof 会返回数组的所有键名,包括数字键名和继承的键名 。
98
96
99
97
对于联合类型,keyof 返回成员共有的键名。
100
98
101
99
``` typescript
102
100
type A = { a: string ; z: boolean };
103
101
type B = { b: string ; z: boolean };
104
102
105
- // 'z'
106
- type KeyT = keyof (A | B );
103
+ // 返回 'z'
104
+ type KeyT = keyof (A | B );
107
105
```
108
106
109
107
对于交叉类型,keyof 返回所有键名。
@@ -113,7 +111,7 @@ type A = { a: string; x: boolean };
113
111
type B = { b: string ; y: number };
114
112
115
113
// 返回 'a' | 'x' | 'b' | 'y'
116
- type KeyT = keyof (A & B );
114
+ type KeyT = keyof (A & B );
117
115
118
116
// 相当于
119
117
keyof (A & B ) ≡ keyof A | keyof B
@@ -229,7 +227,7 @@ JavaScript 语言中,`in`运算符用来确定对象是否包含某个属性
229
227
``` javascript
230
228
const obj = { a: 123 };
231
229
232
- if (' a' in obj)
230
+ if (' a' in obj)
233
231
console .log (' found a' );
234
232
```
235
233
@@ -298,7 +296,7 @@ type A = Person[keyof Obj];
298
296
type T = Person [' notExisted' ]; // 报错
299
297
```
300
298
301
- 如果对象的属性是索引类型,那么方括号运算符的参数可以是属性名的类型 。
299
+ 方括号运算符的参数也可以是属性名的索引类型 。
302
300
303
301
``` typescript
304
302
type Obj = {
@@ -367,7 +365,7 @@ interface Animal {
367
365
interface Dog extends Animal {
368
366
woof(): void ;
369
367
}
370
-
368
+
371
369
// number
372
370
type T1 = Dog extends Animal ? number : string ;
373
371
@@ -396,21 +394,21 @@ type T2 = RegExp extends Animal ? number : string;
396
394
397
395
``` typescript
398
396
// 示例一
399
- type ToArray <Type > =
397
+ type ToArray <Type > =
400
398
Type extends any ? Type [] : never ;
401
399
402
400
// string[]|number[]
403
401
type T = ToArray <string | number >;
404
402
405
403
// 示例二
406
- type ToArray <Type > =
404
+ type ToArray <Type > =
407
405
[Type ] extends [any ] ? Type [] : never ;
408
406
409
407
// (string | number)[]
410
408
type T = ToArray <string | number >;
411
409
```
412
410
413
- 上面的示例一,传入的类型参数是联合类型 ,所以会被展开,返回的也是联合类型。示例二是` extends ` 两侧的运算数都放在方括号里面,所以传入的联合类型不会展示 ,返回的是一个数组。
411
+ 上面的示例一,传入 ` ToArray<Type> ` 的类型参数是一个联合类型 ,所以会被展开,返回的也是联合类型。示例二是` extends ` 两侧的运算数都放在方括号里面,所以传入的联合类型不会展开 ,返回的是一个数组。
414
412
415
413
条件运算符还可以嵌套使用。
416
414
@@ -446,11 +444,11 @@ type Flatten<Type> =
446
444
Type extends Array <infer Item > ? Item : Type ;
447
445
```
448
446
449
- 上面示例中,` Type ` 是外部传入的类型参数,如果传入的是一个数组( ` Array ` ),那么可以从该数组推断出它的成员类型 ,写成` infer Item ` ,表示` Item ` 这个类型参数是从当前信息中推断出来的。
447
+ 上面示例中,` Type ` 是外部传入的类型参数,如果它是数组 ` Array<T> ` 的子类型,那么就将类型变量 ` Item ` 推断为 ` T ` ,即 ` Item ` 代表数组的成员类型 ,写成` infer Item ` ,表示` Item ` 这个类型参数是从当前信息中推断出来的。
450
448
451
449
一旦定义了` Item ` ,后面的代码就可以使用这个类型参数了。
452
450
453
- 下面是这个泛型 ` Flatten<Type> ` 的用法。
451
+ 下面是上例的泛型 ` Flatten<Type> ` 的用法。
454
452
455
453
``` typescript
456
454
// string
@@ -460,7 +458,7 @@ type Str = Flatten<string[]>;
460
458
type Num = Flatten <number >;
461
459
```
462
460
463
- 上面示例中,第一个例子` Flatten<string[]> ` 传入的类型参数是` string[] ` ,可以推断出` Item ` 的类型是` string ` ,所以返回的是` string ` 。第二个例子` Flatten<number> ` 传入的类型参数是` number ` ,它不是数组,所以直接返回本身 。
461
+ 上面示例中,第一个例子` Flatten<string[]> ` 传入的类型参数是` string[] ` ,可以推断出` Item ` 的类型是` string ` ,所以返回的是` string ` 。第二个例子` Flatten<number> ` 传入的类型参数是` number ` ,它不是数组的子类型,所以直接返回自身 。
464
462
465
463
如果不用` infer ` 定义类型参数,那么就要传入两个类型参数。
466
464
@@ -469,7 +467,7 @@ type Flatten<Type, Item> =
469
467
Type extends Array <Item > ? Item : Type ;
470
468
```
471
469
472
- 上面是不用` infer ` 的写法,每次使用` Fleatten ` 的时候,都要传入两个参数,就非常麻烦 。
470
+ 上面是不用` infer ` 的写法,每次使用` Fleatten ` 的时候,都要传入两个参数,就比较麻烦 。
473
471
474
472
下面的例子使用` infer ` ,推断函数的参数类型和返回值类型。
475
473
@@ -482,22 +480,23 @@ type ReturnPromise<T> =
482
480
483
481
上面示例中,如果` T ` 是函数,就返回这个函数的 Promise 版本,否则原样返回。` infer A ` 表示该函数的参数类型为` A ` ,` infer R ` 表示该函数的返回值类型为` R ` 。
484
482
485
- 如果不使用` infer ` ,就不得不把` ReturnPromise<T> ` 写成` ReturnPromise<T, A, R> ` ,这样就很麻烦。
483
+ 如果不使用` infer ` ,就不得不把` ReturnPromise<T> ` 写成` ReturnPromise<T, A, R> ` ,这样就很麻烦,相当于开发者必须人肉推断编译器可以完成的工作 。
486
484
487
485
下面是` infer ` 提取对象指定属性的例子。
488
486
489
487
``` typescript
490
488
type MyType <T > =
491
- T extends {
489
+ T extends {
492
490
a: infer M ,
493
- b: infer N
491
+ b: infer N
494
492
} ? [M , N ] : never ;
495
493
494
+ // 用法示例
495
+ type T = MyType <{ a: string ; b: number }>;
496
496
// [string, number]
497
- type T = MyType <{ a: string ; b: number }>;
498
497
```
499
498
500
- 上面示例中,` infer ` 可以提取参数对象的属性 ` a ` 和属性` b ` 的值 。
499
+ 上面示例中,` infer ` 提取了参数对象的属性 ` a ` 和属性` b ` 的类型 。
501
500
502
501
下面是` infer ` 通过正则匹配提取类型参数的例子。
503
502
@@ -517,7 +516,7 @@ type Bar = Str extends `foo-${infer rest}` ? rest : never // 'bar'
517
516
518
517
` ` ` typescript
519
518
function isFish(
520
- pet : Fish | Bird
519
+ pet : Fish | Bird
521
520
): pet is Fish {
522
521
return (pet as Fish ).swim !== undefined ;
523
522
}
@@ -553,7 +552,7 @@ if (isCat(x)) {
553
552
}
554
553
```
555
554
556
- 上面示例中,需要保证 ` x ` 有 ` meow() ` 方法, ` isCat() ` 的返回值是 ` a is Cat ` 与 ` if ` 结合,就能起到类型保护的作用 ,确保` x ` 是 Cat 类型。
555
+ 上面示例中,函数 ` isCat() ` 的返回类型是 ` a is Cat ` ,它是一个布尔值。后面的 ` if ` 语句就用这个返回值进行判断,从而起到类型保护的作用 ,确保` x ` 是 Cat 类型,从而 ` x.meow() ` 不会报错(假定 ` Cat ` 类型拥有 ` meow() ` 方法) 。
557
556
558
557
` is ` 运算符还有一种特殊用法,就是用在类(class)的内部,描述类的方法的返回值。
559
558
@@ -571,7 +570,7 @@ class Student {
571
570
}
572
571
```
573
572
574
- 上面示例中,` isStudent() ` 方法的返回值类型,取决于该方法内部的` this ` 是否为` Student ` 对象。
573
+ 上面示例中,` isStudent() ` 方法的返回值类型,取决于该方法内部的` this ` 是否为` Student ` 对象。如果是的,就返回布尔值 ` true ` ,否则返回 ` false ` 。
575
574
576
575
注意,` this is T ` 这种写法,只能用来描述方法的返回值类型,而不能用来描述属性的类型。
577
576
@@ -590,17 +589,17 @@ type Greeting = `hello ${World}`;
590
589
591
590
上面示例中,类型` Greeting ` 是一个模板字符串,里面引用了另一个字符串类型` world ` ,因此` Greeting ` 实际上是字符串` hello world ` 。
592
591
593
- 注意,模板字符串可以引用的类型一共6种,分别是 string、number、bigint、boolean、null、undefined。引用其他类型会报错 。
592
+ 注意,模板字符串可以引用的类型一共6种,分别是 string、number、bigint、boolean、null、undefined。引用这6种以外的类型会报错 。
594
593
595
594
``` typescript
596
- type N = 123 ;
597
- type O = { n : 123 };
595
+ type Num = 123 ;
596
+ type Obj = { n : 123 };
598
597
599
- type T1 = ` ${N } received ` ; // 正确
600
- type T2 = ` ${O } received ` ; // 报错
598
+ type T1 = ` ${Num } received ` ; // 正确
599
+ type T2 = ` ${Obj } received ` ; // 报错
601
600
```
602
601
603
- 上面示例中,模板字符串引用数值类型( ` N ` ) 是可以的,但是引用对象类型( ` O ` ) 就会报错。
602
+ 上面示例中,模板字符串引用数值类型的别名 ` Num ` 是可以的,但是引用对象类型的别名 ` Obj ` 就会报错。
604
603
605
604
模板字符串里面引用的类型,如果是一个联合类型,那么它返回的也是一个联合类型,即模板字符串可以展开联合类型。
606
605
@@ -625,3 +624,4 @@ type V = `${T}${U}`;
625
624
```
626
625
627
626
上面示例中,` T ` 和` U ` 都是联合类型,各自有两个成员,模板字符串里面引用了这两个类型,最后得到的就是一个4个成员的联合类型。
627
+
0 commit comments