TypeScript 学习笔记(3):类类型

158 阅读3分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战


为了有效消化知识点,我决定放慢一点速度,今天就学一个:类类型。不贪吃,仔细消化嘿嘿。
下面这个是脑图版的学习笔记 ,关键信息快速 Get 😉 复习也方便,我真棒。

类类型.png

好了,不臭屁了,下面结合具体例子看看今天的知识点 ——

ES6 中正式引入了"类"的概念,使我们能够使用基于类的面向对象方式。下面我们先简单回顾一下的基础用法。

类是啥

类是由 class 关键字定义,可以包含多个属性和方法,类似对象的数据结构。

class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

let greeter = new Greeter("world");

类的继承

通过 extends 关键字,我们可以快速得到一个继承了基类属性和方法的子类,也可以称之为派生类,而基类也称为超类。

//基类
class Animal {
    move(distanceInMeters: number = 0) {
        console.log(`Animal moved ${distanceInMeters}m.`);
    }
}

//派生类
class Dog extends Animal {
    bark() {
        console.log('Woof! Woof!');
    }
}

//Dog实例,拥有 Animal 和 Dog 的属性和方法
const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

类的静态属性和方法

static 关键字用于定义类的静态属性和静态方法,静态属性、静态方法直接存在于类,直接通过类访问,且不会被实例继承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

//直接通过类访问
Foo.classMethod() // 'hello' 

//无法通过实例访问
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

TypeScript 中的类

TypeScript 对类的增强有几个方面:访问修饰符、存取器、抽象类,下面逐个看看。

修饰符

  • public:在任何地方可见、公有的属性或方法(缺省情况下,默认都是 public)
  • private:仅在同一类中可见、私有的属性或方法(只可以在类的内部可见,实例无法访问)
  • protected:仅在类自身及其子类中可见、受保护的属性或方法
  • readonly:可见,但不可修改
class Son {
  public firstName: string;
  private lastName: string = 'Stark';
  constructor(firstName: string) {
    this.firstName = firstName;
    this.lastName; // ok
  }
}

const son = new Son('Tony');
console.log(son.firstName); //  => "Tony"
son.firstName = 'Jack';
console.log(son.firstName); //  => "Jack"
console.log(son.lastName); // ts(2341) Property 'lastName' is private and only accessible within class 'Son'.

存取器

TS 中,可以通过getter、setter截取对类成员的读写访问(同 ES6中的 proxy ?)

抽象类

由 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(); // 错误: 方法在声明的抽象类中不存在

由于不能被实例化,抽象类通常用于对基础逻辑的封装和抽象。

类类型

在声明类的时候,其实也同时声明了一个特殊的类型,这个类型的名字就是类名,表示类实例的类型。

class A {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
const a1: A = {}; // ts(2741) Property 'name' is missing in type '{}' but required in type 'A'.
const a2: A = { name: 'a2' }; // ok

上述例子等同于

class A {
 name: string;
 constructor(name: string) {
   this.name = name;
 }
}

interface AInstanceType { 
   name: string; 
}

const a: AInstanceType = { name: 'a' }

关于类类型和接口的详细说明,参考之前写的 《TypeScript 中的 implements 和 extends》