类的继承 类的继承是使用 extends:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Animal { name: string; constructor (theName: string) { this .name = theName; } move(distanceInMeters: number = 0 ) { console .log(`${this .name} moved ${distanceInMeters} m.` ); } } class Snake extends Animal { constructor (name: string) { super (name); } move(distanceInMeters = 5 ) { console .log("Slithering..." ); super .move(distanceInMeters); } } class Horse extends Animal { constructor (name: string) { super (name); } move(distanceInMeters = 45 ) { console .log("Galloping..." ); super .move(distanceInMeters); } } let sam = new Snake("Sammy the Python" );let tom: Animal = new Horse("Tommy the Palomino" );sam.move(); tom.move(34 );
执行结果:
1 2 3 4 Slithering... Sammy the Python moved 5 m. Galloping... Tommy the Palomino moved 34 m.
以上的例子是 Snake 和 Horse 继承自 Animal,而 Snake 和 Horse 就称为 派生类 / 子类 ,而 Animal 就被称为 基类 / 超类 。
如果派生类内包含了构造函数,在构造函数中必须访问 super()
,它会执行基类的构造函数。尤其是在调用 this
之前,使用super
进行重写了基类的 move 方法。
Public, private, and protected modifiers public 默认值 1 2 3 4 5 6 7 class Animal { public name: string; public constructor(theName: string) { this.name = theName; } public move(distanceInMeters: number) { console.log(`${this.name} moved ${distanceInMeters}m.`); } }
private 当成员被标记成 private时,它就不能在声明它的类的外部访问。
1 2 3 4 5 6 class Animal { private name: string; constructor (theName: string) { this .name = theName; } } new Animal("Cat" ).name;
protected
protected 和 private 很相似,唯一不同的地方是 protected 成员在派生类中是可以被访问的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Person { protected name: string; constructor (name: string) { this .name = name; } } class Employee extends Person { private department: string; constructor (name: string, department: string) { super (name) this .department = department; } public getElevatorPitch() { <!--在这里访问 this .name 是继承自基类的 name --> return `Hello, my name is ${this .name} and I work in ${this .department} .` ; } } let howard = new Employee("Howard" , "Sales" );console .log(howard.getElevatorPitch());console .log(howard.name);
构造函数也可以被标记为 protected,这样的话这个类只能被继承,不能被包含他的类外面实例化。
对于 private 和 protected 申明的成员,如果要比较两个类型是否一样时就会出现不同。 例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Animal { private name: string; constructor (theName: string) { this .name = theName; } } class Rhino extends Animal { constructor () { super ("Rhino" ); } } class Employee { private name: string; constructor (theName: string) { this .name = theName; } } let animal = new Animal("Goat" );let rhino = new Rhino();let employee = new Employee("Bob" );animal = rhino; animal = employee;
因为 Employee 和 Animal 的 name 不是同一个 name
readonly 修饰符 只读属性必须在声明时或构造函数里被初始化。
1 2 3 4 5 6 7 8 9 class Octopus { readonly name: string; readonly numberOfLegs: number = 8 ; constructor (theName: string) { this .name = theName; } } let dad = new Octopus("Man with the 8 strong legs" );dad.name = "Man with the 3-piece suit" ;
参数属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Animal { private name:string; constructor (theName) { this .name = thieName; } move(distanceInMeters: number) { console .log(`${this .name} moved ${distanceInMeters} m.` ); } } class Animal { constructor (private name: string) { } move(distanceInMeters: number) { console .log(`${this .name} moved ${distanceInMeters} m.` ); } }
在构造函数里使用 private name: string
参数来创建和初始化 name
成员,把声明和赋值合并至一处
存取器 使用 get
和 set
设置成为一个存取器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Employee { fullName: string; } let employee = new Employee();employee.fullName = "Bob Smith" ; if (employee.fullName) { console .log(employee.fullName); } let passcode = "secret passcode" ;class Employee { private _fullName: string; get fullName(): string { return this ._fullName; } set fullName(newName: string) { if (passcode && passcode == "secret passcode" ) { this ._fullName = newName; } else { console .log("Error: Unauthorized update of employee!" ); } } } let employee = new Employee();employee.fullName = "Bob Smith" ; if (employee.fullName) { alert(employee.fullName); }
存取器要求你将编译器设置为输出 ECMAScript 5 或更高。 不支持降级到 ECMAScript 3。
只带有 get 不带有 set 的存取器自动被推断为 readonly。 这在从代码生成 .d.ts文件时是有帮助的,因为利用这个属性的用户会看到不允许够改变它的值。
静态属性 通过 static 关键字来设置,上面说的时只有在实例化之后才会初始化的值,静态属性是在类本身里面,它也可以通过 this.
来进行方法问。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Grid { static origin = {x : 0 , y : 0 }; calculateDistanceFromOrigin(point: {x : number; y: number;}) { let xDist = (point.x - Grid.origin.x); let yDist = (point.y - Grid.origin.y); return Math .sqrt(xDist * xDist + yDist * yDist) / this .scale; } constructor (public scale: number) { } } let grid1 = new Grid(1.0 ); let grid2 = new Grid(5.0 ); console .log(grid1.calculateDistanceFromOrigin({x : 10 , y : 10 }));console .log(grid2.calculateDistanceFromOrigin({x : 10 , y : 10 }));
抽象类 使用 abstract 声明。
1 2 3 4 5 6 7 8 9 abstract class Animal { abstract makeSound(): void ; move(): void { console .log('roaming the earch...' ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 abstract class Department { constructor (public name: string) { } printName(): void { console .log('Department name: ' + this .name); } abstract printMeeting(): void ; } class AccountingDepartment extends Department { constructor () { super ('Accounting and Auditing' ); } printMeeting(): void { console .log('The Accounting Department meets each Monday at 10am.' ); } generateReports(): void { console .log('Generating accounting reports...' ); } } let department: Department; department = new Department(); department = new AccountingDepartment(); department.printName(); department.printMeeting(); department.generateReports();
注意点:
允许创建一个抽象对象类型
抽象类不允许实例化
允许对抽象类的子类进行实例化和赋值
抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。定义方法签名但不包含方法体。
抽象方法必须包含 abstract关键字并且可以包含访问修饰符。
高级技巧
构造函数
当你在 ts 中声明了一个类时,就同时声明了很多东西,
例如:
类的实例的 类型
构造函数,这个函数在这个类被 new 的时候调用1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Greeter { greeting: string; constructor (message: string) { this .greeting = message; } greet() { return "Hello, " + this .greeting; } } let greeter: Greeter;greeter = new Greeter("world" ); console .log(greeter.greet());let Greeter = (function ( ) { function Greeter (message ) { this .greeting = message; } Greeter.prototype.greet = function ( ) { return "Hello, " + this .greeting; }; return Greeter; })(); let greeter;greeter = new Greeter("world" ); console .log(greeter.greet());
把类当做接口使用1 2 3 4 5 6 7 8 9 10 class Point { x: number; y: number; } interface Point3d extends Point { z: number; } let point3d: Point3d = {x : 1 , y : 2 , z : 3 };