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 56c894d

Browse files
committedJul 19, 2023
docs: finish chapter class
1 parent 98936e5 commit 56c894d

File tree

3 files changed

+79
-57
lines changed

3 files changed

+79
-57
lines changed
 

‎chapters.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@
77
- symbol.md: symbol 类型
88
- function.md: 函数
99
- object.md: 对象
10+
- interface.md: interface
11+
- class.md:

‎docs/class.md

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Point {
1717
}
1818
```
1919

20-
上面声明时,属性`x``y`的类型都是`number`
20+
上面声明中,属性`x``y`的类型都是`number`
2121

2222
如果不给出类型,TypeScript 会认为`x``y`的类型都是`any`
2323

@@ -43,7 +43,7 @@ class Point {
4343

4444
TypeScript 有一个配置项`strictPropertyInitialization`,只要打开,就会检查属性是否设置了初值,如果没有就报错。
4545

46-
如果你打开了这个设置,但是某些情况下,不是在声明时赋值或在构造函数里面赋值,为了防止这个设置报错,可以使用非空断言。
46+
如果你打开了这个设置,但是某些情况下,不是在声明时赋值或在构造方法里面赋值,为了防止这个设置报错,可以使用非空断言。
4747

4848
```typescript
4949
class Point {
@@ -69,7 +69,7 @@ a.id = 'bar'; // 报错
6969

7070
上面示例中,`id`属性前面有 readonly 修饰符,实例对象修改这个属性就会报错。
7171

72-
readonly 属性的初始值,可以写在顶层属性,也可以写在构造函数里面
72+
readonly 属性的初始值,可以写在顶层属性,也可以写在构造方法里面
7373

7474
```typescript
7575
class A {
@@ -237,7 +237,7 @@ class C {
237237

238238
另外,如果`set`方法的参数没有指定类型,那么会推断为与`get`方法返回值类型一致。
239239

240-
(3)`get`方法与`set`方法的类型必须一致,要么都为公开方法,要么都为私有方法。
240+
(3)`get`方法与`set`方法的可访问性必须一致,要么都为公开方法,要么都为私有方法。
241241

242242
### 属性索引
243243

@@ -247,7 +247,7 @@ class C {
247247
class MyClass {
248248
[s:string]: boolean |
249249
((s:string) => boolean);
250-
250+
251251
get(s:string) {
252252
return this[s] as boolean;
253253
}
@@ -256,25 +256,47 @@ class MyClass {
256256

257257
上面示例中,`[s:string]`表示所有属性名类型为字符串的属性,它们的属性值要么是布尔值,要么是返回布尔值的函数。
258258

259-
注意,由于类的方法是一种特殊属性(属性值为函数的属性),所以属性索引必须同时给出属性和方法两种类型。
259+
注意,由于类的方法是一种特殊属性(属性值为函数的属性),所以属性索引的类型定义也涵盖了方法。如果一个对象同时定义了属性索引和方法,那么前者必须包含后者的类型。
260+
261+
```typescript
262+
class MyClass {
263+
[s:string]: boolean;
264+
f() { // 报错
265+
return true;
266+
}
267+
}
268+
```
269+
270+
上面示例中,属性索引的类型里面不包括方法,导致后面的方法`f()`定义直接报错。正确的写法是下面这样。
271+
272+
```typescript
273+
class MyClass {
274+
[s:string]: boolean | (() => boolean);
275+
f() {
276+
return true;
277+
}
278+
}
279+
```
280+
281+
属性存取器等同于方法,也必须包括在属性索性里面。
260282

261283
```typescript
262284
class MyClass {
263285
[s:string]: boolean;
264-
286+
265287
get(s:string) { // 报错
266288
return this[s] as boolean;
267289
}
268290
}
269291
```
270292

271-
上面示例中,属性索引没有给出方法的类型,导致`get()`方法报错。
293+
上面示例中,属性索引没有给出方法的类型,导致`get()`方法报错。正确的写法就是本节一开始的那个例子。
272294

273295
## 类的 interface 接口
274296

275297
### implements 关键字
276298

277-
interface 接口或 type 别名,可以用对象的形式,为 class 指定一组检查条件。然后,类使用 implements 关键字,表示当前类能够通过这些外部类型条件
299+
interface 接口或 type 别名,可以用对象的形式,为 class 指定一组检查条件。然后,类使用 implements 关键字,表示当前类满足这些外部类型条件的限制
278300

279301
```typescript
280302
interface Country {
@@ -301,7 +323,7 @@ interface 只是指定检查条件,如果不满足这些条件就会报错。
301323
interface A {
302324
get(name:string): boolean;
303325
}
304-
326+
305327
class B implements A {
306328
get(s) { // s 的类型是 any
307329
return true;
@@ -389,15 +411,15 @@ interface Foo {
389411

390412
### 实现多个接口
391413

392-
类可以实现多个接口,每个接口之间使用逗号分隔。
414+
类可以实现多个接口(其实是接受多重限制),每个接口之间使用逗号分隔。
393415

394416
```typescript
395417
class Car implements MotorVehicle, Flyable, Swimmable {
396418
// ...
397419
}
398420
```
399421

400-
上面示例中,`Car`类同时实现了`MotorVehicle``Flyable``Swimmable`三个接口。这意味着,它必须部署这三个接口声明的所有属性和方法。
422+
上面示例中,`Car`类同时实现了`MotorVehicle``Flyable``Swimmable`三个接口。这意味着,它必须部署这三个接口声明的所有属性和方法,满足它们的所有条件
401423

402424
但是,同时实现多个接口并不是一个好的写法,容易使得代码难以管理,可以使用两种方法替代。
403425

@@ -507,7 +529,7 @@ const green:Color = new Color('green');
507529

508530
上面示例中,定义了一个类`Color`。它的类名就代表一种类型,实例对象`green`就属于该类型。
509531

510-
对于引用实例对象的变量来说,既可以声明类型为 Class,也可以声明类型为 Interface,因为两者都代表实例类型
532+
对于引用实例对象的变量来说,既可以声明类型为 Class,也可以声明类型为 Interface,因为两者都代表实例对象的类型
511533

512534
```typescript
513535
interface MotorVehicle {
@@ -560,7 +582,7 @@ function createPoint(
560582
PointClass:typeof Point,
561583
x:number,
562584
y:number
563-
):Point {
585+
):Point {
564586
return new PointClass(x, y);
565587
}
566588
```
@@ -671,7 +693,7 @@ const cust:Customer = new Person();
671693

672694
上面示例中,`Person`类添加了一个属性`age`,跟`Customer`类的结构不再相同。但是这种情况下,TypeScript 依然认为,`Person`属于`Customer`类型。
673695

674-
这是因为根据“结构类型原则”,只要`Person`类具有`name`属性,就满足`Customer`类型的实例结构,所以代替它。反过来就不行,如果`Customer`类多出一个属性,就会报错。
696+
这是因为根据“结构类型原则”,只要`Person`类具有`name`属性,就满足`Customer`类型的实例结构,所以可以代替它。反过来就不行,如果`Customer`类多出一个属性,就会报错。
675697

676698
```typescript
677699
class Person {
@@ -716,7 +738,7 @@ obj instanceof Person // false
716738

717739
```typescript
718740
class Empty {}
719-
741+
720742
function fn(x:Empty) {
721743
// ...
722744
}
@@ -758,7 +780,7 @@ class A {
758780
private name = 'a';
759781
}
760782

761-
class B extends A {
783+
class B extends A {
762784
}
763785

764786
const a:A = new B();
@@ -775,7 +797,7 @@ class B extends A {
775797
const a:A = new B();
776798
```
777799

778-
上面示例中,`A``B`都有私有成员(或保护成员)`name`,这时只有在`B`继承`A`的情况下,`B`才兼容`A`
800+
上面示例中,`A``B`都有私有成员(或保护成员)`name`,这时只有在`B`继承`A`的情况下`class B extends A``B`才兼容`A`
779801

780802
## 类的继承
781803

@@ -787,7 +809,7 @@ class A {
787809
console.log('Hello, world!');
788810
}
789811
}
790-
812+
791813
class B extends A {
792814
}
793815

@@ -820,9 +842,7 @@ class B extends A {
820842
}
821843
```
822844

823-
上面示例中,子类`B`定义了一个方法`greet()`,覆盖了基类`A`的同名方法。
824-
825-
其中,参数`name`省略时,就调用基类`A``greet()`方法,这里可以写成`super.greet()`。使用`super`关键字指代基类是常见做法。
845+
上面示例中,子类`B`定义了一个方法`greet()`,覆盖了基类`A`的同名方法。其中,参数`name`省略时,就调用基类`A``greet()`方法,这里可以写成`super.greet()`,使用`super`关键字指代基类是常见做法。
826846

827847
但是,子类的同名方法不能与基类的类型定义相冲突。
828848

@@ -832,7 +852,7 @@ class A {
832852
console.log('Hello, world!');
833853
}
834854
}
835-
855+
836856
class B extends A {
837857
// 报错
838858
greet(name:string) {
@@ -895,7 +915,7 @@ interface GreeterConstructor {
895915
new (): Greeter;
896916
}
897917

898-
function getGreeterBase(): GreeterConstructor {
918+
function getGreeterBase():GreeterConstructor {
899919
return Math.random() >= 0.5 ? A : B;
900920
}
901921

@@ -1034,7 +1054,7 @@ class B extends A {
10341054
```typescript
10351055
class A {
10361056
private x = 10;
1037-
1057+
10381058
f(obj:A) {
10391059
console.log(obj.x);
10401060
}
@@ -1052,7 +1072,7 @@ a.f(a) // 10
10521072
class A {
10531073
private x = 1;
10541074
}
1055-
1075+
10561076
const a = new A();
10571077
a['x'] // 1
10581078

@@ -1069,7 +1089,7 @@ if ('x' in a) { // 正确
10691089
class A {
10701090
#x = 1;
10711091
}
1072-
1092+
10731093
const a = new A();
10741094
a['x'] // 报错
10751095
```
@@ -1083,9 +1103,9 @@ a['x'] // 报错
10831103
```typescript
10841104
class Singleton {
10851105
private static instance?: Singleton;
1086-
1106+
10871107
private constructor() {}
1088-
1108+
10891109
static getInstance() {
10901110
if (!Singleton.instance) {
10911111
Singleton.instance = new Singleton();
@@ -1101,7 +1121,7 @@ const s = Singleton.getInstance();
11011121

11021122
### protected
11031123

1104-
`protected`修饰符表示该成员是保护成员,只能在类的内部使用该成员,实例无法使用该成员,但是子类可以使用
1124+
`protected`修饰符表示该成员是保护成员,只能在类的内部使用该成员,实例无法使用该成员,但是子类内部可以使用
11051125

11061126
```typescript
11071127
class A {
@@ -1131,7 +1151,7 @@ class A {
11311151
}
11321152

11331153
class B extends A {
1134-
x = 2;
1154+
x = 2;
11351155
}
11361156
```
11371157

@@ -1272,7 +1292,7 @@ class MyClass {
12721292
}
12731293
```
12741294

1275-
`public``protected`静态成员可以被继承
1295+
`public``protected`的静态成员可以被继承
12761296

12771297
```typescript
12781298
class A {
@@ -1294,7 +1314,7 @@ B.getY() // 1
12941314

12951315
## 泛型类
12961316

1297-
类也可以写成泛型,使用类型参数。
1317+
类也可以写成泛型,使用类型参数。关于泛型的详细介绍,请看《泛型》一章。
12981318

12991319
```typescript
13001320
class Box<Type> {
@@ -1304,11 +1324,11 @@ class Box<Type> {
13041324
this.contents = value;
13051325
}
13061326
}
1307-
1327+
13081328
const b:Box<string> = new Box('hello!');
13091329
```
13101330

1311-
上面示例中,类`Box`有类型参数`Type`,因此属于泛型类。新建实例时,变量的类型声明需要带有类型参数的值,不过本例的`Box<string>`可以省略不写,因为可以从等号右边推断得到。
1331+
上面示例中,类`Box`有类型参数`Type`,因此属于泛型类。新建实例时,变量的类型声明需要带有类型参数的值,不过本例等号左边的`Box<string>`可以省略不写,因为可以从等号右边推断得到。
13121332

13131333
注意,静态成员不能使用泛型的类型参数。
13141334

@@ -1318,7 +1338,7 @@ class Box<Type> {
13181338
}
13191339
```
13201340

1321-
上面示例中,静态属性`defaultContents`的类型写成类型参数`Type`会报错。因为这意味着调用时必须给出类型参数`Box<string>.defaultContents`,并且类型参数发生变化,这个属性也会跟着变,这并不是好的做法。
1341+
上面示例中,静态属性`defaultContents`的类型写成类型参数`Type`会报错。因为这意味着调用时必须给出类型参数(即写成`Box<string>.defaultContents`,并且类型参数发生变化,这个属性也会跟着变,这并不是好的做法。
13221342

13231343
## 抽象类,抽象成员
13241344

@@ -1431,9 +1451,7 @@ const b = {
14311451
b.getName() // 'b'
14321452
```
14331453

1434-
上面示例中,变量`a``b``getName()`是同一个方法,但是执行结果不一样,原因就是它们内部的`this`指向不一样的对象。
1435-
1436-
如果`getName()`在变量`a`上运行,`this`指向`a`;如果在`b`上运行,`this`指向`b`
1454+
上面示例中,变量`a``b``getName()`是同一个方法,但是执行结果不一样,原因就是它们内部的`this`指向不一样的对象。如果`getName()`在变量`a`上运行,`this`指向`a`;如果在`b`上运行,`this`指向`b`
14371455

14381456
有些场合需要给出`this`类型,但是 JavaScript 函数通常不带有`this`参数,这时 TypeScript 允许函数增加一个名为`this`的参数,放在参数列表的第一位,用来描述函数内部的`this`关键字的类型。
14391457

@@ -1496,7 +1514,7 @@ class Rectangle {
14961514
public width:number,
14971515
public height:number
14981516
) {}
1499-
1517+
15001518
getAreaFunction() {
15011519
return function () {
15021520
return this.width * this.height; // 报错
@@ -1548,8 +1566,9 @@ class FileSystemObject {
15481566
}
15491567
```
15501568

1551-
上面示例中,两个方法的返回值类型都是布尔值,写成`this is Type`的形式,可以精确表示返回值。
1569+
上面示例中,两个方法的返回值类型都是布尔值,写成`this is Type`的形式,可以精确表示返回值。`is`运算符的介绍详见《类型断言》一章。
15521570

15531571
## 参考链接
15541572

15551573
- [TypeScript Constructor in Interface](http://fritzthecat-blog.blogspot.com/2018/06/typescript-constructor-in-interface.html)
1574+

‎docs/interface.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 简介
44

5-
interface 是对象的模板,可以看作是一种类型约定,中文译为“接口”。使用了这个模板的对象,就拥有了指定的类型结构。
5+
interface 是对象的模板,可以看作是一种类型约定,中文译为“接口”。使用了某个模板的对象,就拥有了指定的类型结构。
66

77
```typescript
88
interface Person {
@@ -92,7 +92,7 @@ interface A {
9292
```typescript
9393
interface MyObj {
9494
[prop: string]: number;
95-
95+
9696
a: boolean; // 编译错误
9797
}
9898
```
@@ -105,7 +105,7 @@ interface MyObj {
105105
interface A {
106106
[prop: number]: string;
107107
}
108-
108+
109109
const obj:A = ['a', 'b', 'c'];
110110
```
111111

@@ -141,7 +141,7 @@ interface A {
141141

142142
// 写法二
143143
interface B {
144-
f: (x: boolean) => string;
144+
f: (x: boolean) => string;
145145
}
146146

147147
// 写法三
@@ -154,7 +154,7 @@ interface C {
154154

155155
```typescript
156156
const f = 'f';
157-
157+
158158
interface A {
159159
[f](x: boolean): string;
160160
}
@@ -238,7 +238,7 @@ interface 可以使用`extends`关键字,继承其他 interface。
238238
interface Shape {
239239
name: string;
240240
}
241-
241+
242242
interface Circle extends Shape {
243243
radius: number;
244244
}
@@ -254,11 +254,11 @@ interface 允许多重继承。
254254
interface Style {
255255
color: string;
256256
}
257-
257+
258258
interface Shape {
259259
name: string;
260260
}
261-
261+
262262
interface Circle extends Style, Shape {
263263
radius: number;
264264
}
@@ -274,7 +274,7 @@ interface Circle extends Style, Shape {
274274
interface Foo {
275275
id: string;
276276
}
277-
277+
278278
interface Bar extends Foo {
279279
id: number; // 报错
280280
}
@@ -288,7 +288,7 @@ interface Bar extends Foo {
288288
interface Foo {
289289
id: string;
290290
}
291-
291+
292292
interface Bar {
293293
id: number;
294294
}
@@ -401,7 +401,7 @@ interface Document {
401401
foo: string;
402402
}
403403

404-
document.foo = 'hello';
404+
document.foo = 'hello';
405405
```
406406

407407
上面示例中,接口`Document`增加了一个自定义属性`foo`,从而就可以在`document`对象上使用自定义属性。
@@ -510,15 +510,15 @@ declare const s: Circle | Rectangle;
510510
s.area; // bigint | number
511511
```
512512

513-
上面示例中,接口`Circle``Rectangle`组成一个联合类型`Circle | Rectangle`。因此,这个联合类型的同名属性`area`,也是一个联合类型。本例中的`declare`命令表示变量`s`的具体定义,由其他脚本文件给出,详见《d.ts 文件》一章。
513+
上面示例中,接口`Circle``Rectangle`组成一个联合类型`Circle | Rectangle`。因此,这个联合类型的同名属性`area`,也是一个联合类型。本例中的`declare`命令表示变量`s`的具体定义,由其他脚本文件给出,详见《declare 命令》一章。
514514

515515
## interface 与 type 的异同
516516

517517
`interface`命令与`type`命令作用类似,都可以表示对象类型。
518518

519519
很多对象类型即可以用 interface 表示,也可以用 type 表示。而且,两者往往可以换用,几乎所有的 interface 命令都可以改写为 type 命令。
520520

521-
它们的相似之处,首先表示在都能为对象类型起名
521+
它们的相似之处,首先表现在都能为对象类型起名
522522

523523
```typescript
524524
type Country = {
@@ -549,8 +549,8 @@ type Animal = {
549549
name: string
550550
}
551551

552-
type Bear = Animal & {
553-
honey: boolean
552+
type Bear = Animal & {
553+
honey: boolean
554554
}
555555
```
556556
@@ -692,4 +692,5 @@ type AorBwithName = AorB & {
692692

693693
上面示例中,类型`AorB`是一个联合类型,`AorBwithName`则是为`AorB`添加一个属性。这两种运算,`interface`都没法表达。
694694

695-
综上所述,如果有复杂的类型运算,那么没有选择只有使用`type`;一般情况下,`interface`灵活性比较高,便于扩充类型或自动合并,建议优先使用。
695+
综上所述,如果有复杂的类型运算,那么没有其他选择只能使用`type`;一般情况下,`interface`灵活性比较高,便于扩充类型或自动合并,建议优先使用。
696+

0 commit comments

Comments
 (0)
Please sign in to comment.