类
基本例子
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
我们声明了一个Greeter类,它包含三个成员:一个greeting属性,一个greet方法,一个构造函数。最后一行,我们使用new构造了Greeter类的一个实例。 它会调用之前定义的构造函数,创建一个Greeter类型的新对象,并执行构造函数初始化它。
继承
类允许继承,基本例子
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();
在这个例子中,类从基类中继承了属性和方法,在这里,Dog是一个派生类,派生自Animal基类,派生类通常被称作子类,基类通常被称作超类。 复杂例子
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Horse extends Animal {
constructor(name: string) {
// 必须先调用super函数
super(name);
}
// 重写父类的方法
move(distanceInMeters = 45) {
console.log("Galloping...");
super.move(distanceInMeters);
}
}
let tom: Animal = new Horse("Tommy the Palomino");
tom.move(34);
在这个例子中,子类中多了构造函数,当子类中包含构造函数时,必须 要先调用super(),它会执行超类的构造函数,并且一定要在构造函数访问this之前。子类还可以重写父类的方法。 所以,tom虽然被声明为animal类型,但它的值时House,所以执行的move方法是House中的move方法。
Galloping...
Tommy the Palomino moved 34m.
公共,私有与受保护的修饰符
public
默认为public,不需要特别去标记
private
私有属性不可以在类的外部被访问。
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // 错误: 'name' 是私有的.
protected
protected属性与私有属性比较相似,不同的是,protected可以在派生类中被访问,
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() {
// 派生类可以访问父类中的protected属性
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属性不可在类的外部访问。
readonly
只读属性,只读属性必须在声明时或构造函数里被初始化
参数属性
class Animal {
constructor(private name: string) { }
move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
将原来需要在构造函数声明之前定义的属性直接在构造函数里使用private name: string参数来创建和初始化name成员,把声明和赋值合并至一处。
存取器
TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
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。
静态属性
静态属性是存在于类本身的成员,而不是在类的实例中,不需要在类的实例化时才被初始化。 访问静态属性,需要在属性名前加上类名,如同在实例属性上使用this.前缀来访问属性一样。
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); // 1x scale
let grid2 = new Grid(5.0); // 5x scale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
抽象类
抽象类一般是其他派生类的基类,一般不会实例化。 abstract 关键字主要是用于定义抽象类和抽象类中的方法,它的语法与接口的语法很相似,两者都是定义方法签名但不包含方法体。 不同于接口的是:抽象类中的抽象方法不包含具体实现但必须在派生类中实现;抽象类可以包含成员的实现细节;抽象方法必须包含abstract关键字并且可以包含访问修饰符。
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'); // 在派生类的构造函数中必须调用 super()
}
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(); // 错误: 方法在声明的抽象类中不存在
高级类
构造函数
当你在TypeScript里声明了一个类的时候,实际上同时声明了很多东西。 首先就是类的实例的类型。
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
// 声明greeter类的实例的类型是Greeter
let greeter: Greeter;
greeter = new Greeter("world");
console.log(greeter.greet());
这里,我们写了let greeter: Greeter,意思是Greeter类的实例的类型是Greeter。
我们也创建了一个叫做构造函数的值。 这个函数会在我们使用new创建类实例的时候被调用。 下面我们来看看,上面的代码被编译成JavaScript后是什么样子的:
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());
上面的代码里,let Greeter将被赋值为构造函数。 当我们调用new并执行了这个函数后,便会得到一个类的实例。 这个构造函数也包含了类的所有静态属性。 换个角度说,我们可以认为类具有实例部分与静态部分这两个部分。