@@ -17,7 +17,7 @@ class Point {
17
17
}
18
18
```
19
19
20
- 上面声明时 ,属性` x ` 和` y ` 的类型都是` number ` 。
20
+ 上面声明中 ,属性` x ` 和` y ` 的类型都是` number ` 。
21
21
22
22
如果不给出类型,TypeScript 会认为` x ` 和` y ` 的类型都是` any ` 。
23
23
@@ -43,7 +43,7 @@ class Point {
43
43
44
44
TypeScript 有一个配置项` strictPropertyInitialization ` ,只要打开,就会检查属性是否设置了初值,如果没有就报错。
45
45
46
- 如果你打开了这个设置,但是某些情况下,不是在声明时赋值或在构造函数里面赋值 ,为了防止这个设置报错,可以使用非空断言。
46
+ 如果你打开了这个设置,但是某些情况下,不是在声明时赋值或在构造方法里面赋值 ,为了防止这个设置报错,可以使用非空断言。
47
47
48
48
``` typescript
49
49
class Point {
@@ -69,7 +69,7 @@ a.id = 'bar'; // 报错
69
69
70
70
上面示例中,` id ` 属性前面有 readonly 修饰符,实例对象修改这个属性就会报错。
71
71
72
- readonly 属性的初始值,可以写在顶层属性,也可以写在构造函数里面 。
72
+ readonly 属性的初始值,可以写在顶层属性,也可以写在构造方法里面 。
73
73
74
74
``` typescript
75
75
class A {
@@ -237,7 +237,7 @@ class C {
237
237
238
238
另外,如果` set ` 方法的参数没有指定类型,那么会推断为与` get ` 方法返回值类型一致。
239
239
240
- (3)` get ` 方法与` set ` 方法的类型必须一致 ,要么都为公开方法,要么都为私有方法。
240
+ (3)` get ` 方法与` set ` 方法的可访问性必须一致 ,要么都为公开方法,要么都为私有方法。
241
241
242
242
### 属性索引
243
243
@@ -247,7 +247,7 @@ class C {
247
247
class MyClass {
248
248
[s : string ]: boolean |
249
249
((s : string ) => boolean );
250
-
250
+
251
251
get(s : string ) {
252
252
return this [s ] as boolean ;
253
253
}
@@ -256,25 +256,47 @@ class MyClass {
256
256
257
257
上面示例中,` [s:string] ` 表示所有属性名类型为字符串的属性,它们的属性值要么是布尔值,要么是返回布尔值的函数。
258
258
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
+ 属性存取器等同于方法,也必须包括在属性索性里面。
260
282
261
283
``` typescript
262
284
class MyClass {
263
285
[s : string ]: boolean ;
264
-
286
+
265
287
get(s : string ) { // 报错
266
288
return this [s ] as boolean ;
267
289
}
268
290
}
269
291
```
270
292
271
- 上面示例中,属性索引没有给出方法的类型,导致` get() ` 方法报错。
293
+ 上面示例中,属性索引没有给出方法的类型,导致` get() ` 方法报错。正确的写法就是本节一开始的那个例子。
272
294
273
295
## 类的 interface 接口
274
296
275
297
### implements 关键字
276
298
277
- interface 接口或 type 别名,可以用对象的形式,为 class 指定一组检查条件。然后,类使用 implements 关键字,表示当前类能够通过这些外部类型条件 。
299
+ interface 接口或 type 别名,可以用对象的形式,为 class 指定一组检查条件。然后,类使用 implements 关键字,表示当前类满足这些外部类型条件的限制 。
278
300
279
301
``` typescript
280
302
interface Country {
@@ -301,7 +323,7 @@ interface 只是指定检查条件,如果不满足这些条件就会报错。
301
323
interface A {
302
324
get(name : string ): boolean ;
303
325
}
304
-
326
+
305
327
class B implements A {
306
328
get(s ) { // s 的类型是 any
307
329
return true ;
@@ -389,15 +411,15 @@ interface Foo {
389
411
390
412
### 实现多个接口
391
413
392
- 类可以实现多个接口,每个接口之间使用逗号分隔。
414
+ 类可以实现多个接口(其实是接受多重限制) ,每个接口之间使用逗号分隔。
393
415
394
416
``` typescript
395
417
class Car implements MotorVehicle , Flyable , Swimmable {
396
418
// ...
397
419
}
398
420
```
399
421
400
- 上面示例中,` Car ` 类同时实现了` MotorVehicle ` 、` Flyable ` 、` Swimmable ` 三个接口。这意味着,它必须部署这三个接口声明的所有属性和方法。
422
+ 上面示例中,` Car ` 类同时实现了` MotorVehicle ` 、` Flyable ` 、` Swimmable ` 三个接口。这意味着,它必须部署这三个接口声明的所有属性和方法,满足它们的所有条件 。
401
423
402
424
但是,同时实现多个接口并不是一个好的写法,容易使得代码难以管理,可以使用两种方法替代。
403
425
@@ -507,7 +529,7 @@ const green:Color = new Color('green');
507
529
508
530
上面示例中,定义了一个类` Color ` 。它的类名就代表一种类型,实例对象` green ` 就属于该类型。
509
531
510
- 对于引用实例对象的变量来说,既可以声明类型为 Class,也可以声明类型为 Interface,因为两者都代表实例类型 。
532
+ 对于引用实例对象的变量来说,既可以声明类型为 Class,也可以声明类型为 Interface,因为两者都代表实例对象的类型 。
511
533
512
534
``` typescript
513
535
interface MotorVehicle {
@@ -560,7 +582,7 @@ function createPoint(
560
582
PointClass : typeof Point ,
561
583
x : number ,
562
584
y : number
563
- ): Point {
585
+ ): Point {
564
586
return new PointClass (x , y );
565
587
}
566
588
```
@@ -671,7 +693,7 @@ const cust:Customer = new Person();
671
693
672
694
上面示例中,` Person ` 类添加了一个属性` age ` ,跟` Customer ` 类的结构不再相同。但是这种情况下,TypeScript 依然认为,` Person ` 属于` Customer ` 类型。
673
695
674
- 这是因为根据“结构类型原则”,只要` Person ` 类具有` name ` 属性,就满足` Customer ` 类型的实例结构,所以代替它 。反过来就不行,如果` Customer ` 类多出一个属性,就会报错。
696
+ 这是因为根据“结构类型原则”,只要` Person ` 类具有` name ` 属性,就满足` Customer ` 类型的实例结构,所以可以代替它 。反过来就不行,如果` Customer ` 类多出一个属性,就会报错。
675
697
676
698
``` typescript
677
699
class Person {
@@ -716,7 +738,7 @@ obj instanceof Person // false
716
738
717
739
``` typescript
718
740
class Empty {}
719
-
741
+
720
742
function fn(x : Empty ) {
721
743
// ...
722
744
}
@@ -758,7 +780,7 @@ class A {
758
780
private name = ' a' ;
759
781
}
760
782
761
- class B extends A {
783
+ class B extends A {
762
784
}
763
785
764
786
const a: A = new B ();
@@ -775,7 +797,7 @@ class B extends A {
775
797
const a: A = new B ();
776
798
```
777
799
778
- 上面示例中,` A ` 和` B ` 都有私有成员(或保护成员)` name ` ,这时只有在` B ` 继承` A ` 的情况下,` B ` 才兼容` A ` 。
800
+ 上面示例中,` A ` 和` B ` 都有私有成员(或保护成员)` name ` ,这时只有在` B ` 继承` A ` 的情况下( ` class B extends A ` ) ,` B ` 才兼容` A ` 。
779
801
780
802
## 类的继承
781
803
@@ -787,7 +809,7 @@ class A {
787
809
console .log (' Hello, world!' );
788
810
}
789
811
}
790
-
812
+
791
813
class B extends A {
792
814
}
793
815
@@ -820,9 +842,7 @@ class B extends A {
820
842
}
821
843
```
822
844
823
- 上面示例中,子类` B ` 定义了一个方法` greet() ` ,覆盖了基类` A ` 的同名方法。
824
-
825
- 其中,参数` name ` 省略时,就调用基类` A ` 的` greet() ` 方法,这里可以写成` super.greet() ` 。使用` super ` 关键字指代基类是常见做法。
845
+ 上面示例中,子类` B ` 定义了一个方法` greet() ` ,覆盖了基类` A ` 的同名方法。其中,参数` name ` 省略时,就调用基类` A ` 的` greet() ` 方法,这里可以写成` super.greet() ` ,使用` super ` 关键字指代基类是常见做法。
826
846
827
847
但是,子类的同名方法不能与基类的类型定义相冲突。
828
848
@@ -832,7 +852,7 @@ class A {
832
852
console .log (' Hello, world!' );
833
853
}
834
854
}
835
-
855
+
836
856
class B extends A {
837
857
// 报错
838
858
greet(name : string ) {
@@ -895,7 +915,7 @@ interface GreeterConstructor {
895
915
new (): Greeter ;
896
916
}
897
917
898
- function getGreeterBase(): GreeterConstructor {
918
+ function getGreeterBase(): GreeterConstructor {
899
919
return Math .random () >= 0.5 ? A : B ;
900
920
}
901
921
@@ -1034,7 +1054,7 @@ class B extends A {
1034
1054
``` typescript
1035
1055
class A {
1036
1056
private x = 10 ;
1037
-
1057
+
1038
1058
f(obj : A ) {
1039
1059
console .log (obj .x );
1040
1060
}
@@ -1052,7 +1072,7 @@ a.f(a) // 10
1052
1072
class A {
1053
1073
private x = 1 ;
1054
1074
}
1055
-
1075
+
1056
1076
const a = new A ();
1057
1077
a [' x' ] // 1
1058
1078
@@ -1069,7 +1089,7 @@ if ('x' in a) { // 正确
1069
1089
class A {
1070
1090
#x = 1 ;
1071
1091
}
1072
-
1092
+
1073
1093
const a = new A ();
1074
1094
a [' x' ] // 报错
1075
1095
```
@@ -1083,9 +1103,9 @@ a['x'] // 报错
1083
1103
``` typescript
1084
1104
class Singleton {
1085
1105
private static instance? : Singleton ;
1086
-
1106
+
1087
1107
private constructor () {}
1088
-
1108
+
1089
1109
static getInstance() {
1090
1110
if (! Singleton .instance ) {
1091
1111
Singleton .instance = new Singleton ();
@@ -1101,7 +1121,7 @@ const s = Singleton.getInstance();
1101
1121
1102
1122
### protected
1103
1123
1104
- ` protected ` 修饰符表示该成员是保护成员,只能在类的内部使用该成员,实例无法使用该成员,但是子类可以使用 。
1124
+ ` protected ` 修饰符表示该成员是保护成员,只能在类的内部使用该成员,实例无法使用该成员,但是子类内部可以使用 。
1105
1125
1106
1126
``` typescript
1107
1127
class A {
@@ -1131,7 +1151,7 @@ class A {
1131
1151
}
1132
1152
1133
1153
class B extends A {
1134
- x = 2 ;
1154
+ x = 2 ;
1135
1155
}
1136
1156
```
1137
1157
@@ -1272,7 +1292,7 @@ class MyClass {
1272
1292
}
1273
1293
```
1274
1294
1275
- ` public ` 和` protected ` 静态成员可以被继承 。
1295
+ ` public ` 和` protected ` 的静态成员可以被继承 。
1276
1296
1277
1297
``` typescript
1278
1298
class A {
@@ -1294,7 +1314,7 @@ B.getY() // 1
1294
1314
1295
1315
## 泛型类
1296
1316
1297
- 类也可以写成泛型,使用类型参数。
1317
+ 类也可以写成泛型,使用类型参数。关于泛型的详细介绍,请看《泛型》一章。
1298
1318
1299
1319
``` typescript
1300
1320
class Box <Type > {
@@ -1304,11 +1324,11 @@ class Box<Type> {
1304
1324
this .contents = value ;
1305
1325
}
1306
1326
}
1307
-
1327
+
1308
1328
const b: Box <string > = new Box (' hello!' );
1309
1329
```
1310
1330
1311
- 上面示例中,类` Box ` 有类型参数` Type ` ,因此属于泛型类。新建实例时,变量的类型声明需要带有类型参数的值,不过本例的 ` Box<string> ` 可以省略不写,因为可以从等号右边推断得到。
1331
+ 上面示例中,类` Box ` 有类型参数` Type ` ,因此属于泛型类。新建实例时,变量的类型声明需要带有类型参数的值,不过本例等号左边的 ` Box<string> ` 可以省略不写,因为可以从等号右边推断得到。
1312
1332
1313
1333
注意,静态成员不能使用泛型的类型参数。
1314
1334
@@ -1318,7 +1338,7 @@ class Box<Type> {
1318
1338
}
1319
1339
```
1320
1340
1321
- 上面示例中,静态属性` defaultContents ` 的类型写成类型参数` Type ` 会报错。因为这意味着调用时必须给出类型参数` Box<string>.defaultContents ` ,并且类型参数发生变化,这个属性也会跟着变,这并不是好的做法。
1341
+ 上面示例中,静态属性` defaultContents ` 的类型写成类型参数` Type ` 会报错。因为这意味着调用时必须给出类型参数(即写成 ` Box<string>.defaultContents ` ) ,并且类型参数发生变化,这个属性也会跟着变,这并不是好的做法。
1322
1342
1323
1343
## 抽象类,抽象成员
1324
1344
@@ -1431,9 +1451,7 @@ const b = {
1431
1451
b .getName () // 'b'
1432
1452
```
1433
1453
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 ` 。
1437
1455
1438
1456
有些场合需要给出` this ` 类型,但是 JavaScript 函数通常不带有` this ` 参数,这时 TypeScript 允许函数增加一个名为` this ` 的参数,放在参数列表的第一位,用来描述函数内部的` this ` 关键字的类型。
1439
1457
@@ -1496,7 +1514,7 @@ class Rectangle {
1496
1514
public width : number ,
1497
1515
public height : number
1498
1516
) {}
1499
-
1517
+
1500
1518
getAreaFunction() {
1501
1519
return function () {
1502
1520
return this .width * this .height ; // 报错
@@ -1548,8 +1566,9 @@ class FileSystemObject {
1548
1566
}
1549
1567
```
1550
1568
1551
- 上面示例中,两个方法的返回值类型都是布尔值,写成` this is Type ` 的形式,可以精确表示返回值。
1569
+ 上面示例中,两个方法的返回值类型都是布尔值,写成` this is Type ` 的形式,可以精确表示返回值。` is ` 运算符的介绍详见《类型断言》一章。
1552
1570
1553
1571
## 参考链接
1554
1572
1555
1573
- [ TypeScript Constructor in Interface] ( http://fritzthecat-blog.blogspot.com/2018/06/typescript-constructor-in-interface.html )
1574
+
0 commit comments