Skip to content

Latest commit

 

History

History
316 lines (239 loc) · 7.24 KB

类类型.md

File metadata and controls

316 lines (239 loc) · 7.24 KB

类类型

类本身就是一种类型,类的名字可以直接作为类型名:

// 定义类
class TypeA {
  // ...
}

// 声明TypeA类型
let a: TypeA;
// 赋值TypeA类型
a = new TypeA();

语法扩展

属性扩展

在ES6中,实例属性和静态属性不能直接定义在类内部:

// 下面是ES6代码

// 不合法的定义
class Greeter {
  // 错误,实例属性不能定义在类里
  greeting = 'world';
  // 错误,静态属性不能定义在类里
  static greeting = 'world';
}

// 合法的定义
class Greeter {
  constructor(){
    // 正确,ES6中实例属性只能定义在构造器内部
    this.greeting = 'world';
  }
}
// 正确,ES6中静态属性只能定义在类外部
Greeter.greeting = 'world';

而在TypeScript中,下面的定义是合法的:

// 下面是TypeScript代码

class Greeter {
  // 定义实例属性并初始化
  greeting: string = 'world';

  // 定义静态属性并初始化
  static greeting: string = 'world';
}

访问控制 public/private/protected 修饰符

在TypeScript中,修饰符不是必须的,他们主要用来控制类成员的可访问性,类成员包括:

  • 实例属性
  • 静态属性
  • 实例方法
  • 静态方法
  • 构造函数
  • getter/setter

如果你没有显式为成员添加访问控制修饰符,那么将默认为 public

class Animal {
  // name属性未显式添加修饰符,默认为public
  age: number; 

  // 显式添加 private
  private name: string;

  // show方法未显式添加修饰符,默认为public
  show(){ 
    // ...
  }
}

// 与上述定义等价
class Animal {
  public age: number;
  private name: string;
  public show() {
    // ...
  }
}

当成员被标记成 private 时,它不能在声明它的类外部访问。比如:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

// 错误: 不能在类外访问私有成员
// error TS2341: Property 'name' is private and only accessible within class 'Animal'
new Animal("Cat").name;

protected 修饰符与 private 修饰符的行为相似,都不能在类的外部访问。但有一点不同, 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() {
    // 正确,来自父类的name成员在派生类里可以访问。虽然它位于Persion类的外部
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
  }
}

let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());

// 错误,受保护的成员 name 不能在外部访问
// error TS2445: Property 'name' is protected and only accessible within class 'Person' and its subclasses
console.log(howard.name); 

访问控制修饰符可直接用于构造函数的参数声明,这是一个语法糖,可以非常方便的实现属性的定义并初始化:

class Animal {
  // private修饰符用于构造器的name参数前
  constructor(private name: string) {}
}

// 以上定义相当于
class Animal {
  private name: string;
  constructor(name: string) {
    this.name = name;
  }
}

只读修饰符 readonly

你可以使用 readonly 关键字将属性设置为只读的。 只读属性必须声明时构造函数里被初始化

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");

// 错误! name 是只读的
// error TS2540: Cannot assign to 'name' because it is a constant or a read-only property
dad.name = "Man with the 3-piece suit";

只读属性和常量类似,一旦初始化,之后就再也不允许被赋值。

抽象类和抽象方法

用于描述抽象类和抽象方法的关键字都是 abstract,抽象方法没有方法体(也就是不包含实现):

// Animal是抽象类
abstract class Animal {
  // makeSound是抽象方法,没有方法体
  abstract makeSound(): void;
  move(): void {
      console.log('roaming the earch...');
  }
}

一般情况下,抽象类和抽象方法是同时出现的,但也有例外:

  1. 一个类包含抽象方法,那么这个类必须是抽象类
  2. 抽象类可以没有抽象方法
class Animal {
  // 错误,有抽象方法,但是类不是抽象类,不符合[1]
  // error TS1244: Abstract methods can only appear within an abstract class
  abstract makeSound(): void;
  move(): void {
      console.log('roaming the earch...');
  }
}

// 正确,抽象类可以没有抽象方法,符合[2]
// 没有抽象方法的抽象类和一般类没区别
abstract class Animal {
  move(): void {
      console.log('roaming the earch...');
  }
}

抽象类主要是用来被继承使用,抽象方法必须在派生类中必须被实现:

// Animal是抽象类
abstract class Animal {
  // makeSound是抽象方法,没有方法体
  abstract makeSound(): void;
  move(): void {
      console.log('roaming the earch...');
  }
}

// 错误,抽象方法makeSound没有实现
// error TS2515: Non-abstract class 'Dog' does not implement inherited abstract member 'makeSound' from class 'Animal'
class Dog extends Animal {
  // 空类
}

// 正确,抽象方法被实现
class Dog extends Animal {
  makeSound(): void{
    // ...
  }
}

构造器类型

官方文档把构造器类型称为类的静态类型,字面意思比较难理解

构造器类型用来表示具有相同构造器的类,主要是用来描述类的构造者。可以简单的理解为构造器类型的值就是一个类,而本章开篇所讲的类类型的值是一个对象

语法:

new (p1: T1, p2: T2, ...) => T

可以看到,构造器类型的语法和函数类型极为相似,区别是在最前面多了一个 new 关键字:

class TypeA {
  constructor(name: string) {
    // ...
  }
}

// 变量b为构造器类型,和类TypeA的构造器兼容
let b: new (name: string) => TypeA;
b = TypeA;
// b现在是一个类
new b('type');

上面的例子实际上相当于为类 TypeA 定义了一个别名 b。下面再看一个例子:

class TypeA {
  constructor(name: string) {
    // ...
  }
}

// TypeB 类多一个方法 show
class TypeB {
  constructor(name: string) {
    // ...
  }
  show(){
    // ...
  }
}

let c: new (name: string) => TypeA;
// 正确,TypeB除了构造器和TypeA兼容,也兼容TypeA的所有成员
c = TypeB;

let d: new (name: string) => TypeB;
// 错误,TypeA缺少方法show,不能直接赋值给d
// error TS2322: Type 'typeof TypeA' is not assignable to type 'new (name: string) => TypeB'
d = TypeA;

构造器类型在日常开发中使用较少,由于概念比较晦涩,建议只要能看懂语法就行,无须深入