Inheritance
extends
、super
class Animal {
name: string
constructor(name: string) {
this.name = name
}
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name)
}
bark() {
super.move()
console.log("Woof! Woof!");
}
}
const dog = new Dog('fjdsl')
dog.bark()
Public, private, and protected modifiers
Public by default
默认情况下,所有属性为 public
。
class Animal {
public name: string;
public constructor(theName: string) {
this.name = theName;
}
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
TypeScript’s private
只能在类的内部访问,子类不能访问
class Animal {
private name: string;
constructor(theName: string) {
this.name = theName;
}
}
new Animal("Cat").name; // 外部访问不了,会报错
TypeScript是一种结构类型系统。当我们比较两种不同类型时,无论它们来自何处,如果所有成员的类型都是兼容的,那么我们就说这些类型本身是兼容的。
但是对于private
、protected
的比较会有所不同。他们的必须来源于同一个地方。
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;
Rhino 的 私有成员 name 跟 Animal 来源是一样的,都是 Animal 内部声明的,所以 Rhino 的实例可以赋值给 Animal 的实例。但是对于 Employee 它的私有成员是自己另外声明的,来源不同,所以 Employee 的类型跟 Animal 不一样,所以后者的实例不能赋值给前者。
Understanding protected
跟 private
差不多,但是子类能够访问该属性
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() {
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); // Error Property 'name' is protected and only accessible within class 'Person' and its subclasses
可以给 constructor 声明 private
、protected
。
- private 表示该类不能被在外部被实例化,即不能被
extend
,只能在内容通过静态成员的方式进行实例化 - protected 表示不能再外部实例化,可以在子类中被实例化
class Person {
protected name: string;
protected constructor(theName: string) {
this.name = theName;
}
}
// Employee can extend Person
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
let john = new Person("John");
Readonly modifier
您可以使用readonly关键字将属性设置为只读。只读属性必须在其声明或构造函数中进行初始化。
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor(theName: string) {
this.name = theName;
}
}
Parameter properties
帮我们简化声明了属性之后,又在构造函数中对其进行复制的过程。支持的修饰符有public
、private
、protected
、readonly
class Person {
constructor(
public readonly name: string,
private age: number,
protected height: number
) {
}
}
const person = new Person('xxx', 11, 21)
Accessors
给某个成员设置 getters/setters
。如果只设置 getter 相当于 readonly
。
class Employee {
private _fullName: string = "";
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (newName && newName.length > 10) {
throw new Error("fullName has a max length of " + fullNameMaxLength);
}
this._fullName = newName;
}
}
Static Properties
- 所有该类的实例都可以通过
类.静态属性
来访问静态属性,且可以修改。 - 在类的实例上不会包含静态属性。由于 static 属性是挂载在原型上的,所以访问时通过 this.xxx 也是可以访问的。
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
Grid.origin = { x: 1, y: 2 }
console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 }));
console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 10 }));
Abstract Classes
- abstract 类是派生类的基类
- 不能直接被实例化
- 跟 interface 不同的是, abstract 类可能会包含成员的具体实现
-
abstract
关键词不仅可以用来声明抽象 class,还可以在 class 内部使用它声明抽象方法
- 抽象类中的抽象方法在子类中必须实现
abstract class Department {
constructor(public name: string) {}
printName(): void {
console.log("Department name: " + this.name);
}
abstract printMeeting(): void; // must be implemented in derived classes
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
console.log("Generating accounting reports...");
}
}
let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: Property 'generateReports' does not exist on type 'Department'
Advanced Techniques
Constructor functions
class Greeter {
static x: number = 11
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
let greeter: Greeter; // Greeter 当做类型使用
greeter = new Greeter("world"); // 类型匹配
let gree: typeof Greeter = new Greeter("word") // error:前者包括了静态类型,后者为实例类型不包括静态类型
let newGree: typeof Greeter = Greeter // newGree 跟 Greeter 一模一样
- 声明的
Greeter
也是一个类型。可以当做一个类型来使用。该类型是该Greeter
的实例类型。 - 因为是实例,所以不会包含静态成员的类型。只能通过
typeof Greeter
来获取整个类的类型(静态+实例
) - 声明的
Greeter
也是一个构造函数,因为被转换为 ES5 后其实就是function Greeter{}
Using a class as an interface
因为前面说过,创建 class 时会创建一个代表该 class 实例的类型,所以 class 可以当做类型,也可以被 interface 继承
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = { x: 1, y: 2, z: 3 };