一、类的基本使用

1.定义

  1. class Greeter {
  2. greeting: string;
  3. constructor(message: string) {
  4. this.greeting = message;
  5. }
  6. greet() {
  7. return "Hello, " + this.greeting;
  8. }
  9. }
  10. let greeter = new Greeter("world");

这个类有三个成员:一个greeting属性,一个构造函数,一个greet方法,我们通过this访问类的成员。
最后通过new构造了一个类的实例。

这个代码编译成 js 代码就是如下:

  1. var Greeter = /** @class */ (function () {
  2. function Greeter(message) {
  3. this.greeting = message;
  4. }
  5. Greeter.prototype.greet = function () {
  6. return "Hello, " + this.greeting;
  7. };
  8. return Greeter;
  9. }());
  10. var greeter = new Greeter("world");

可以看到编译后的类就是 函数+原型链的形式。

2.继承

在 Typescript 中使用extends关键字来定义类的继承:

  1. class Father{
  2. fatherName: string = 'fatherName';
  3. name: string; // 自己、自己的子类、自己的孙子类都能访问
  4. age: number; // 自己和自己的子类能访问,其他类不能访问
  5. money: number; // 只有自己能访问,子类和其他类不能访问
  6. constructor(name: string, age: number, money: number){
  7. this.name = name
  8. this.age = age
  9. this.money = money
  10. }
  11. getName():string{
  12. return this.name
  13. }
  14. toString(){
  15. console.log('Father')
  16. }
  17. }
  18. class Child extends Father{
  19. childName: string = 'childName';
  20. constructor(name: string, age: number, money: number){
  21. super(name, age, money)
  22. }
  23. desc(){
  24. console.log(this.name, this.age)
  25. }
  26. toString(){ // 子类和父类有重名方法时,子类会覆盖父类
  27. console.log('Child')
  28. }
  29. }
  30. let child = new Child('yf', 10, 11)
  31. console.log(child.name)

Child 通过 extends 继承了 Father,并且在Child 的constructor中调用了super()方法,他会执行基类的构造函数,而且是在访问this的属性之前调用 super() 方法,这是Typescript 继承的关键,这样Child的实例就会继承Father 的所有能够被继承的属性和方法(有些类的修饰符修饰的属性不能被继承,我们后面会说到)。

二、类的修饰符

1.公共,私有与受保护的修饰符

(1)public

通过public 修饰符指定的成员是在任何地方都可见的,也就说他自己、自己的子类、自己的孙子类都能访问到,在Typescript 中 成员都是默认为 public 的。

  1. class Father{
  2. public name: string; // 自己、自己的子类、自己的孙子类都能访问
  3. constructor(name: string, age: number, money: number){
  4. this.name = name
  5. }
  6. getName():string{
  7. return this.name
  8. }
  9. toString(){
  10. console.log('Father')
  11. }
  12. }
  13. class Child extends Father{
  14. constructor(name: string, age: number, money: number){
  15. super(name)
  16. }
  17. }
  18. let child = new Child('yf')
  19. console.log(child.name) // yf

上面定义的public类通过子类继承之后,子类的实例也能访问到。

(2)private

private 私有的,他修饰的成员只能由他自己访问,子类和其他类都不能访问。

  1. class Father{
  2. public name: string; // 自己、自己的子类、自己的孙子类都能访问
  3. private money: number; // 只有自己能访问,子类和其他类不能访问
  4. constructor(name: string, money: number){
  5. this.name = name
  6. this.money = money
  7. }
  8. getName():string{
  9. return this.name
  10. }
  11. toString(){
  12. console.log('Father')
  13. }
  14. }
  15. const father = new Father('leah', 10);
  16. console.log(father.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)
  17. class Child extends Father{
  18. constructor(name: string, money: number){
  19. super(name, money)
  20. }
  21. desc(){
  22. console.log(this.name, this.money) // Property 'money' is private and only accessible within class 'Father'.
  23. console.log(this.name)
  24. }
  25. toString(){ // 子类和父类有重名方法时,子类会覆盖父类
  26. console.log('Child')
  27. }
  28. }
  29. let child = new Child('yf', 11)
  30. console.log(child.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)

通过上面的例子可以看到,private 修饰的成员只money能在Father 这个类里面调用,不能在Father类的实例以及子类中调用,调用就会报错 Property ‘money’ is private and only accessible within class ‘Father’.ts(2341)。

(3)protected

protected 修饰的成员只能被自己和自己的子类访问,其他类不能访问。

  1. class Father{
  2. static fatherName: string = 'fatherName';
  3. public name: string; // 自己、自己的子类、自己的孙子类都能访问
  4. protected age: number; // 自己和自己的子类能访问,其他类不能访问
  5. private money: number; // 只有自己能访问,子类和其他类不能访问
  6. constructor(name: string, age: number, money: number){
  7. this.name = name
  8. this.age = age
  9. this.money = money
  10. }
  11. getName():string{
  12. return this.name
  13. }
  14. toString(){
  15. console.log('Father')
  16. }
  17. }
  18. const father = new Father('leah', 20, 10);
  19. console.log(father.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)
  20. console.log(father.age) // Property 'age' is protected and only accessible within class 'Father' and its subclasses.ts(2445)
  21. class Child extends Father{
  22. static childName: string = 'childName';
  23. constructor(name: string, age: number, money: number){
  24. super(name, age, money)
  25. }
  26. desc(){
  27. // console.log(this.name, this.age, this.money) // Property 'money' is private and only accessible within class 'Father'.
  28. console.log(this.name, this.age)
  29. }
  30. toString(){ // 子类和父类有重名方法时,子类会覆盖父类
  31. console.log('Child')
  32. }
  33. }
  34. Child.fatherName; // 子类继承了父类的静态方法
  35. Child.childName;
  36. let child = new Child('yf', 10, 11)
  37. console.log(child.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)
  38. console.log(child.age) // Property 'age' is protected and only accessible within class 'Father' and its subclasses.ts(2445)

age 被 protected修饰之后,就只能在他的父类和子类中访问,不能在 父类的实例和子类的实例中访问,访问就会报错 Property ‘age’ is protected and only accessible within class ‘Father’ and its subclasses.ts(2445)

protected 还可以用来修饰 constructor 构造函数,被protected 指定的constructor只能被子类继承,不能用来创建实例。

  1. class Test {
  2. protected constructor(){
  3. }
  4. }
  5. const t = new Test() // Constructor of class 'Test' is protected and only accessible within the class declaration.ts(2674)
  6. class ChildTest extends Test{
  7. constructor(){
  8. super()
  9. }
  10. }
  11. const Ct = new ChildTest()

2.readonly

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

  1. class Father{
  2. static fatherName: string = 'fatherName';
  3. public name: string; // 自己、自己的子类、自己的孙子类都能访问
  4. protected age: number; // 自己和自己的子类能访问,其他类不能访问
  5. private money: number; // 只有自己能访问,子类和其他类不能访问
  6. readonly hight: number;
  7. constructor(name: string, age: number, money: number, hight: number){
  8. this.name = name
  9. this.age = age
  10. this.money = money
  11. this.hight = hight
  12. }
  13. getName():string{
  14. return this.name
  15. }
  16. toString(){
  17. console.log('Father')
  18. }
  19. }
  20. const father = new Father('leah', 20, 10, 150);

三、类的属性

1.参数属性

在上面我们先是通过修饰符初始化了一个属性,然后在构造函数里用接收的参数对属性进行赋值,这个过程可以用参数属性进行简化,就是在构造函数的参数前面加上访问修饰符。

  1. class Father{
  2. constructor(name: string, protected age: number, private money: number, readonly hight: number){
  3. this.name = name
  4. this.age = age
  5. this.money = money
  6. this.hight = hight
  7. }
  8. getName():string{
  9. return this.name
  10. }
  11. toString(){
  12. console.log('Father')
  13. }
  14. }

参数属性可以方便地让我们在一个地方定义并初始化一个成员。

2.静态属性

静态属性通过 static 关键字来指定,被指定的这个成员不会被实例继承,而是直接通过类来调用.

  1. class Father{
  2. static fatherName: string = 'fatherName';
  3. constructor(name: string, protected age: number, private money: number, readonly height: number){
  4. this.name = name
  5. this.age = age
  6. this.money = money
  7. this.height = height
  8. }
  9. }
  10. const father = new Father('leah', 20, 10, 150);
  11. console.log(father.fatherName) // Property 'fatherName' does not exist on type 'Father'. Did you mean to access the static member 'Father.fatherName' instead?ts(2576)
  12. console.log(Father.fatherName)

父类的静态属性/方法,可以被子类继承

  1. class Father{
  2. static fatherName: string = 'fatherName';
  3. constructor(name: string, protected age: number, private money: number, readonly height: number){
  4. this.name = name
  5. this.age = age
  6. this.money = money
  7. this.height = height
  8. }
  9. }
  10. const father = new Father('leah', 20, 10, 150);
  11. console.log(father.fatherName) // Property 'fatherName' does not exist on type 'Father'. Did you mean to access the static member 'Father.fatherName' instead?ts(2576)
  12. console.log(Father.fatherName)
  13. class Child extends Father{
  14. static childName: string = 'childName';
  15. constructor(name: string, age: number, money: number, height: number){
  16. super(name, age, money, height)
  17. }
  18. }
  19. console.log(Child.fatherName); // 子类继承了父类的静态方法
  20. console.log(Child.childName);
  21. let child = new Child('yf', 10, 11, 150)
  22. console.log(child.fatherName) // Property 'fatherName' does not exist on type 'Child'. Did you mean to access the static member 'Child.fatherName' instead?ts(2576)

3.可选属性

可选属性表示可以传也可以不穿参数。

  1. class Test {
  2. name?: string;
  3. constructor(name?: string){
  4. this.name = name
  5. }
  6. }
  7. const test1 = new Test()
  8. const test2 = new Test('leah')

四、存取器

通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。

  1. class UserInfo {
  2. name: string = '';
  3. constructor(){}
  4. get getName(){
  5. return this.name
  6. }
  7. set setName(name: string){
  8. this.name = name
  9. }
  10. }

五、抽象类

抽象类一般用来被其他类继承使用,而不是直接实例化使用。
使用abstract 关键字定义抽象类:

  1. abstract class People{
  2. constructor(public name: string){}
  3. abstract getName(): string
  4. }
  5. class Student extends People{
  6. constructor(name: string){
  7. super(name)
  8. this.name = name
  9. }
  10. getName(){
  11. return this.name
  12. }
  13. }
  14. const student = new Student('leah')
  15. student.getName()
  16. const people = new People() // Cannot create an instance of an abstract class.ts(2511)

在 People 类里面使用abstract 定义了一个抽象方法 getName,并且指定了这个方法的返回类型,也可以定义方法的参数类型。然后在 派生类 Student 里面实现了这个抽象方法,最后再通过Student 的实例调用这个抽象方法。
当对抽象类 People 进行实例化的时候报错了,所以抽象只能被子类继承了,实例化之后才能访问。

注意,必须是在派生类中实现了这个抽象方法的细节之后才能访问,不让不能访问。

  1. abstract class People{
  2. constructor(public name: string){}
  3. abstract getName(): string
  4. }
  5. class Student extends People{ // Non-abstract class 'Student' does not implement inherited abstract member 'getName' from class 'People'.ts(2515)
  6. constructor(name: string){
  7. super(name)
  8. this.name = name
  9. }
  10. // getName(){
  11. // return this.name
  12. // }
  13. }
  14. const student = new Student('leah')
  15. student.getName()

在定义Student 类的时候报错了,抽象类里定义的抽象方法不会被子类继承,必须在在子类中实现该方法的定义。

  1. /**
  2. * 获取站点信息
  3. */
  4. protected getSiteInfo(): SiteInfo{
  5. return new SiteInfo({
  6. appId: this.config.appId,
  7. appVersion: this.config.appVersion,
  8. version,
  9. platform: this.platformTye,
  10. uid: this.globalUUID,
  11. sid: this.globalSessionId,
  12. userId: this.config.userId,
  13. ext: this.config.ext,
  14. device: this.deviceInfo,
  15. pageInfo: this.getPageInfo()
  16. })
  17. }
  18. /**
  19. * 获取页面信息
  20. */
  21. protected getPageInfo(): PageInfo

在父类中定义了getPageInfo函数,我们不希望父组件去实现这个函数,而是由子组件实现,此时这种写法会报错,Function implementation is missing or not immediately following the declaration. 意思是 这个函数并没有被实现,要想不报错,只需要加个 abstract 关键字。

  1. /**
  2. * 获取页面信息
  3. */
  4. protected abstract getPageInfo(): PageInfo

六、类与接口

1.类继承接口

  1. interface Animal{
  2. name: string
  3. }
  4. class Dog implements Animal{
  5. // name: string // 可以这样声明也可以在构造函数中声明。
  6. constructor(public name: string){}
  7. }

定义一个Animal 接口,这个接口有一个 name 属性,定义的类 Dog 要继承这个接口,类继承接口必须使用 implements 关键字。(如果是类继承类、接口继承接口就直接使用extends 关键字)。类实现接口之后,必须声明接口中定义的属性以及方法。

一个类可以继承多个接口,且必须要将所有接口的所有属性和方法都实现了。

  1. interface Animal{
  2. name: string
  3. }
  4. interface Carnivore{
  5. type: string
  6. }
  7. class Tiger implements Animal, Carnivore{
  8. // name: string
  9. // type: string
  10. constructor(public name:string, public type:string){}
  11. }

2.接口继承类

  1. class Animal{
  2. name: string;
  3. }
  4. interface A extends Animal{}
  5. class B implements A{} // Property 'name' is missing in type 'B' but required in type 'Animal'.ts(2420)
  6. class C implements A{ // 必须要将接口中包含的所有属性和方法都声明了才可以,不然编译会报错
  7. name: string
  8. }

接口会继承类的成员。

七、在泛型中使用类

  1. const create = <T>(c: { new (): T }): T => {
  2. return new c();
  3. };
  4. class Info {
  5. age: number;
  6. }
  7. create(Info).age;
  8. create(Info).name; // error 类型“Info”上不存在属性“name”

创建了一个create函数,传入的参数是一个类,返回的是一个类创建的实例
参数c的类型定义中,new() 代表调用类的构造函数,他的类型也就是类创建的实例的类型
return new c() 表示使用传进来的 类 c 创建一个实例并返回。