Typescript 类

修饰符

public

public:公有属性方法修饰符,这是默认修饰符;用来指定在创建实例后可以通过实例访问的,也就是类定义的外部可以访问的属性和方法。

private

private:私有修饰符,它修饰的属性在类的定义外面无法访问;

class Parent {
  private age: number;
  constructor(age: number) {
    this.age = age;
  }
}
const p = new Parent(18);
console.log(p); // { age: 18 }
console.log(p.age); // error 属性“age”为私有属性,只能在类“Parent”中访问
console.log(Parent.age); // error 类型“typeof ParentA”上不存在属性“age”
class Child extends Parent {
  constructor(age: number) {
    super(age);
    console.log(super.age); // 通过 "super" 关键字只能访问基类的公共方法和受保护方法
  }
}

protected

protected 修饰符是受保护修饰符,和 private 有些相似,但有一点不同,protected 修饰的成员在继承该类的子类中可以访问

class Parent {
  protected age: number;
  constructor(age: number) {
    this.age = age;
  }
  protected getAge() {
    return this.age;
  }
}
const p = new Parent(18);
console.log(p.age); // error 属性“age”为私有属性,只能在类“ParentA”中访问
console.log(Parent.age); // error 类型“typeof ParentA”上不存在属性“age”
class Child extends Parent {
  constructor(age: number) {
    super(age);
    console.log(super.age); // undefined
    console.log(super.getAge());
  }
}
new Child(18)

protected 还能用来修饰 constructor 构造函数,加了 protected 修饰符之后,这个类就不能再用来创建实例,只能被子类继承

class Parent {
  protected constructor() {  }
}
const p = new Parent(); // error 类“Parent”的构造函数是受保护的,仅可在类声明中访问
class Child extends Parent {
  constructor() {
    super();
  }
}
const c = new Child();

readonly

设置为只读的属性,实例只能读取这个属性值,但不能修改

class UserInfo {
  readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}
const user = new UserInfo("Lison");
user.name = "haha"; // error Cannot assign to 'name' because it is a read-only property

参数属性

在定义类 B 时,构造函数有一个参数 name,这个 name 使用访问修饰符 public 修饰,此时即为 name 声明了参数属性,也就无需再显示地在类中初始化这个属性了。

class A {
  constructor(name: string) {}
}
const a = new A("aaa");
console.log(a.name); // error 类型“A”上不存在属性“name”
class B {
  constructor(public name: string) {}
}
const b = new B("bbb");
console.log(b.name); // "bbb"

静态属性

使用static关键字来指定属性或方法是静态的,实例将不会添加这个静态属性,也不会继承这个静态方法,你可以使用修饰符和 static 关键字来指定一个属性或方法

class Parent {
  public static age: number = 18;
  public static getAge() {
    return Parent.age;
  }
  constructor() {
    //
  }
}
const p = new Parent();
console.log(p.age); // error Property 'age' is a static member of type 'Parent'
console.log(Parent.age); // 1

// 使用了 private 修饰
class Parent {
  public static getAge() {
    return Parent.age;
  }
  private static age: number = 18;
  constructor() {
    //
  }
}
const p = new Parent();
console.log(p.age); // error Property 'age' is a static member of type 'Parent'
console.log(Parent.age); // error 属性“age”为私有属性,只能在类“Parent”中访问。

可选类属性

class Info {
  name: string;
  age?: number;
  constructor(name: string, age?: number, public sex?: string) {
    this.name = name;
    this.age = age;
  }
}
const info1 = new Info("lison");
const info2 = new Info("lison", 18);
const info3 = new Info("lison", 18, "man");

抽象类

抽象类一般用来被其他类继承,而不直接用它创建实例。抽象类和类内部定义抽象方法,使用 abstract 关键字。

abstract 关键字不仅可以标记类和类里面的方法,还可以标记类中定义的属性和存取器

abstract class People {
  constructor(public name: string) {}
  abstract printName(): void;
}
class Man extends People {
  constructor(name: string) {
    super(name);
    this.name = name;
  }
  printName() {
    console.log(this.name);
  }
}
const m = new Man(); // error 应有 1 个参数,但获得 0 个
const man = new Man("lison");
man.printName(); // 'lison'
const p = new People("lison"); // error 无法创建抽象类的实例

// 在抽象类里定义的抽象方法,在子类中是不会继承的,所以在子类中必须实现该方法的定义
abstract class People {
  constructor(public name: string) {}
  abstract printName(): void;
}
class Man extends People {
  // error 非抽象类“Man”不会实现继承自“People”类的抽象成员"printName"
  constructor(name: string) {
    super(name);
    this.name = name;
  }
}
const m = new Man("lison");
m.printName(); // error m.printName is not a function

// 标记类中定义的属性和存取器
abstract class People {
  abstract name: string;
  abstract get insideName(): string;
  abstract set insideName(value: string);
}
class Pp extends People {
  name: string;
  insideName: string;
}

实例类型

当我们定义一个类,并创建实例后,这个实例的类型就是创建他的类

class People {
  constructor(public name: string) {}
}
let p: People = new People("lison");

类类型接口

接口 FoodInterface 要求使用该接口的值必须有一个 type 属性,定义的类 FoodClass 要使用接口,需要使用关键字implements。

implements关键字用来指定一个类要继承的接口,如果是接口和接口、类和类直接的继承,使用extends,如果是类继承接口,则用implements。

interface FoodInterface {
  type: string;
}
class FoodClass implements FoodInterface {
  // error Property 'type' is missing in type 'FoodClass' but required in type 'FoodInterface'
  static type: string;
  constructor() {}
}

// 接口检测的是使用该接口定义的类创建的实例,所以上面例子中虽然定义了静态属性 type,但静态属性不会添加到实例上,所以还是报错,所以我们可以这样改:
interface FoodInterface {
  type: string;
}
class FoodClass implements FoodInterface {
  constructor(public type: string) {}
}
// 等同于:
abstract class FoodAbstractClass {
  abstract type: string;
}
class Food extends FoodAbstractClass {
  constructor(public type: string) {
    super();
  }
}

接口继承类

接口可以继承一个类,当接口继承了该类后,会继承类的成员,但是不包括其实现,也就是只继承成员以及成员类型。接口还会继承类的private和protected修饰的成员,当接口继承的这个类中包含这两个修饰符修饰的成员时,这个接口只可被这个类或他的子类实现。

class A {
  protected name: string;
}
interface I extends A {}
class B implements I {} // error Property 'name' is missing in type 'B' but required in type 'I'
class C implements I {
  // error 属性“name”受保护,但类型“C”并不是从“A”派生的类
  name: string;
}
class D extends A implements I {
  getName() {
    return this.name;
  }
}

在泛型中使用类类型

在这个例子里,我们创建了一个一个 create 函数,传入的参数是一个类,返回的是一个类创建的实例,这里有几个点要讲: 参数 c 的类型定义中,new()代表调用类的构造函数,他的类型也就是类创建实例后的实例的类型。 return new c()这里使用传进来的类 c 创建一个实例并返回,返回的实例类型也就是函数的返回值类型。

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