Skip to content

Commit 5bbb132

Browse files
committedJul 22, 2023
docs: finish chapter operator
1 parent 8097846 commit 5bbb132

File tree

2 files changed

+36
-35
lines changed

2 files changed

+36
-35
lines changed
 

‎chapters.yml

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
- generics.md: 泛型
1313
- enum.md: Enum 类型
1414
- assert.md: 类型断言
15+
- operator.md: 运算符

‎docs/operator.md

+35-35
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,20 @@ interface T {
3131
type KeyT = keyof T; // 0 | 'a' | 'b'
3232
```
3333

34-
由于 JavaScript 对象的键名只有三种类型,所以对于任意键名的联合类型就是`string|number|symbol`
34+
由于 JavaScript 对象的键名只有三种类型,所以对于任意对象的键名的联合类型就是`string|number|symbol`
3535

3636
```typescript
3737
// string | number | symbol
3838
type KeyT = keyof any;
3939
```
4040

41-
对于上面三种类型以外的类型使用 keyof 运算符,返回`never`类型,表示不可能有这样类型的键名。
41+
对于没有自定义键名的类型使用 keyof 运算符,返回`never`类型,表示不可能有这样类型的键名。
4242

4343
```typescript
4444
type KeyT = keyof object; // never
4545
```
4646

47-
上面示例中,由于不可能有`object`类型的键名,所以`keyof object`返回`never`类型。
47+
上面示例中,由于`object`类型没有自身的属性,也就没有键名,所以`keyof object`返回`never`类型。
4848

4949
由于 keyof 返回的类型是`string|number|symbol`,如果有些场合只需要其中的一种类型,那么可以采用交叉类型的写法。
5050

@@ -54,9 +54,7 @@ type Capital<T extends string> = Capitalize<T>;
5454
type MyKeys<Obj extends object> = Capital<keyof Obj>; // 报错
5555
```
5656

57-
上面示例中,类型`Capital`只接受字符串作为类型参数,传入`keyof Obj`会报错,原因是这时的类型参数是`string|number|symbol`,跟字符串不兼容。
58-
59-
采用下面的交叉类型写法,就不会报错。
57+
上面示例中,类型`Capital`只接受字符串作为类型参数,传入`keyof Obj`会报错,原因是这时的类型参数是`string|number|symbol`,跟字符串不兼容。采用下面的交叉类型写法,就不会报错。
6058

6159
```typescript
6260
type MyKeys<Obj extends object> = Capital<string & keyof Obj>;
@@ -89,21 +87,21 @@ type KeyT = keyof T;
8987
如果 keyof 运算符用于数组或元组类型,得到的结果可能出人意料。
9088

9189
```typescript
90+
type Result = keyof ['a', 'b', 'c'];
9291
// 返回 number | "0" | "1" | "2"
9392
// | "length" | "pop" | "push" | ···
94-
type Result = keyof ['a', 'b', 'c'];
9593
```
9694

97-
上面示例中,keyof 会返回数组的所有属性名,包括字符串属性名和继承的属性名
95+
上面示例中,keyof 会返回数组的所有键名,包括数字键名和继承的键名
9896

9997
对于联合类型,keyof 返回成员共有的键名。
10098

10199
```typescript
102100
type A = { a: string; z: boolean };
103101
type B = { b: string; z: boolean };
104102

105-
// 'z'
106-
type KeyT = keyof (A | B);
103+
// 返回 'z'
104+
type KeyT = keyof (A | B);
107105
```
108106

109107
对于交叉类型,keyof 返回所有键名。
@@ -113,7 +111,7 @@ type A = { a: string; x: boolean };
113111
type B = { b: string; y: number };
114112

115113
// 返回 'a' | 'x' | 'b' | 'y'
116-
type KeyT = keyof (A & B);
114+
type KeyT = keyof (A & B);
117115

118116
// 相当于
119117
keyof (A & B) ≡ keyof A | keyof B
@@ -229,7 +227,7 @@ JavaScript 语言中,`in`运算符用来确定对象是否包含某个属性
229227
```javascript
230228
const obj = { a: 123 };
231229

232-
if ('a' in obj)
230+
if ('a' in obj)
233231
console.log('found a');
234232
```
235233

@@ -298,7 +296,7 @@ type A = Person[keyof Obj];
298296
type T = Person['notExisted']; // 报错
299297
```
300298

301-
如果对象的属性是索引类型,那么方括号运算符的参数可以是属性名的类型
299+
方括号运算符的参数也可以是属性名的索引类型
302300

303301
```typescript
304302
type Obj = {
@@ -367,7 +365,7 @@ interface Animal {
367365
interface Dog extends Animal {
368366
woof(): void;
369367
}
370-
368+
371369
// number
372370
type T1 = Dog extends Animal ? number : string;
373371

@@ -396,21 +394,21 @@ type T2 = RegExp extends Animal ? number : string;
396394

397395
```typescript
398396
// 示例一
399-
type ToArray<Type> =
397+
type ToArray<Type> =
400398
Type extends any ? Type[] : never;
401399

402400
// string[]|number[]
403401
type T = ToArray<string|number>;
404402

405403
// 示例二
406-
type ToArray<Type> =
404+
type ToArray<Type> =
407405
[Type] extends [any] ? Type[] : never;
408406

409407
// (string | number)[]
410408
type T = ToArray<string|number>;
411409
```
412410

413-
上面的示例一,传入的类型参数是联合类型,所以会被展开,返回的也是联合类型。示例二是`extends`两侧的运算数都放在方括号里面,所以传入的联合类型不会展示,返回的是一个数组。
411+
上面的示例一,传入`ToArray<Type>`的类型参数是一个联合类型,所以会被展开,返回的也是联合类型。示例二是`extends`两侧的运算数都放在方括号里面,所以传入的联合类型不会展开,返回的是一个数组。
414412

415413
条件运算符还可以嵌套使用。
416414

@@ -446,11 +444,11 @@ type Flatten<Type> =
446444
Type extends Array<infer Item> ? Item : Type;
447445
```
448446

449-
上面示例中,`Type`是外部传入的类型参数,如果传入的是一个数组(`Array`),那么可以从该数组推断出它的成员类型,写成`infer Item`,表示`Item`这个类型参数是从当前信息中推断出来的。
447+
上面示例中,`Type`是外部传入的类型参数,如果它是数组`Array<T>`的子类型,那么就将类型变量`Item`推断为`T`,即`Item`代表数组的成员类型,写成`infer Item`,表示`Item`这个类型参数是从当前信息中推断出来的。
450448

451449
一旦定义了`Item`,后面的代码就可以使用这个类型参数了。
452450

453-
下面是这个泛型`Flatten<Type>`的用法。
451+
下面是上例的泛型`Flatten<Type>`的用法。
454452

455453
```typescript
456454
// string
@@ -460,7 +458,7 @@ type Str = Flatten<string[]>;
460458
type Num = Flatten<number>;
461459
```
462460

463-
上面示例中,第一个例子`Flatten<string[]>`传入的类型参数是`string[]`,可以推断出`Item`的类型是`string`,所以返回的是`string`。第二个例子`Flatten<number>`传入的类型参数是`number`它不是数组,所以直接返回本身
461+
上面示例中,第一个例子`Flatten<string[]>`传入的类型参数是`string[]`,可以推断出`Item`的类型是`string`,所以返回的是`string`。第二个例子`Flatten<number>`传入的类型参数是`number`它不是数组的子类型,所以直接返回自身
464462

465463
如果不用`infer`定义类型参数,那么就要传入两个类型参数。
466464

@@ -469,7 +467,7 @@ type Flatten<Type, Item> =
469467
Type extends Array<Item> ? Item : Type;
470468
```
471469

472-
上面是不用`infer`的写法,每次使用`Fleatten`的时候,都要传入两个参数,就非常麻烦
470+
上面是不用`infer`的写法,每次使用`Fleatten`的时候,都要传入两个参数,就比较麻烦
473471

474472
下面的例子使用`infer`,推断函数的参数类型和返回值类型。
475473

@@ -482,22 +480,23 @@ type ReturnPromise<T> =
482480

483481
上面示例中,如果`T`是函数,就返回这个函数的 Promise 版本,否则原样返回。`infer A`表示该函数的参数类型为`A``infer R`表示该函数的返回值类型为`R`
484482

485-
如果不使用`infer`,就不得不把`ReturnPromise<T>`写成`ReturnPromise<T, A, R>`,这样就很麻烦。
483+
如果不使用`infer`,就不得不把`ReturnPromise<T>`写成`ReturnPromise<T, A, R>`,这样就很麻烦,相当于开发者必须人肉推断编译器可以完成的工作
486484

487485
下面是`infer`提取对象指定属性的例子。
488486

489487
```typescript
490488
type MyType<T> =
491-
T extends {
489+
T extends {
492490
a: infer M,
493-
b: infer N
491+
b: infer N
494492
} ? [M, N] : never;
495493

494+
// 用法示例
495+
type T = MyType<{ a: string; b: number }>;
496496
// [string, number]
497-
type T = MyType<{ a: string; b: number }>;
498497
```
499498

500-
上面示例中,`infer`可以提取参数对象的属性`a`和属性`b`的值
499+
上面示例中,`infer`提取了参数对象的属性`a`和属性`b`的类型
501500

502501
下面是`infer`通过正则匹配提取类型参数的例子。
503502

@@ -517,7 +516,7 @@ type Bar = Str extends `foo-${infer rest}` ? rest : never // 'bar'
517516
518517
```typescript
519518
function isFish(
520-
pet:Fish|Bird
519+
pet: Fish|Bird
521520
):pet is Fish {
522521
return (pet as Fish).swim !== undefined;
523522
}
@@ -553,7 +552,7 @@ if (isCat(x)) {
553552
}
554553
```
555554

556-
上面示例中,需要保证`x``meow()`方法,`isCat()`的返回值是`a is Cat``if`结合,就能起到类型保护的作用,确保`x`是 Cat 类型。
555+
上面示例中,函数`isCat()`的返回类型是`a is Cat`,它是一个布尔值。后面的`if`语句就用这个返回值进行判断,从而起到类型保护的作用,确保`x`是 Cat 类型,从而`x.meow()`不会报错(假定`Cat`类型拥有`meow()`方法)
557556

558557
`is`运算符还有一种特殊用法,就是用在类(class)的内部,描述类的方法的返回值。
559558

@@ -571,7 +570,7 @@ class Student {
571570
}
572571
```
573572

574-
上面示例中,`isStudent()`方法的返回值类型,取决于该方法内部的`this`是否为`Student`对象。
573+
上面示例中,`isStudent()`方法的返回值类型,取决于该方法内部的`this`是否为`Student`对象。如果是的,就返回布尔值`true`,否则返回`false`
575574

576575
注意,`this is T`这种写法,只能用来描述方法的返回值类型,而不能用来描述属性的类型。
577576

@@ -590,17 +589,17 @@ type Greeting = `hello ${World}`;
590589

591590
上面示例中,类型`Greeting`是一个模板字符串,里面引用了另一个字符串类型`world`,因此`Greeting`实际上是字符串`hello world`
592591

593-
注意,模板字符串可以引用的类型一共6种,分别是 string、number、bigint、boolean、null、undefined。引用其他类型会报错
592+
注意,模板字符串可以引用的类型一共6种,分别是 string、number、bigint、boolean、null、undefined。引用这6种以外的类型会报错
594593

595594
```typescript
596-
type N = 123;
597-
type O = { n : 123 };
595+
type Num = 123;
596+
type Obj = { n : 123 };
598597

599-
type T1 = `${N} received`; // 正确
600-
type T2 = `${O} received`; // 报错
598+
type T1 = `${Num} received`; // 正确
599+
type T2 = `${Obj} received`; // 报错
601600
```
602601

603-
上面示例中,模板字符串引用数值类型(`N`是可以的,但是引用对象类型(`O`就会报错。
602+
上面示例中,模板字符串引用数值类型的别名`Num`是可以的,但是引用对象类型的别名`Obj`就会报错。
604603

605604
模板字符串里面引用的类型,如果是一个联合类型,那么它返回的也是一个联合类型,即模板字符串可以展开联合类型。
606605

@@ -625,3 +624,4 @@ type V = `${T}${U}`;
625624
```
626625

627626
上面示例中,`T``U`都是联合类型,各自有两个成员,模板字符串里面引用了这两个类型,最后得到的就是一个4个成员的联合类型。
627+

0 commit comments

Comments
 (0)
Please sign in to comment.