TypeScript 中的类相比于 ES6 :
class Person {
name: string; // 校验
sex: string;
constructor(name: string, sex: string) { // 校验
this.name = name; // 前面定义的属性必须进行初始化,在构造函数内初始化或者使用默认值,否则会报错
this.sex = sex
}
}
属性默认值
class Person7 {
name: string = 'aa';
constructor(name: string) {
// this.name = name // 设置默认值之后,可以不需要在这里初始化赋值
}
}
可选属性
class Person7 {
name?: string;
constructor(name: string) {
// this.name = name // 设置可选属性之后,可以不需要在这里初始化赋值
}
}
静态属性
静态属性,指的是定义在类本身上面的属性或方法。可以通过类名直接访问这个属性。
class Person {
static aa:string = 'woshishui';
name: string;
constructor(name: string) {
this.name = name;
}
saySomething() {
console.log(Person13.aa) // 通过类名来访问这个静态属性
}
}
继承
class Man extends Person {
constructor(name: string, sex: string) {
super(name,sex)
}
}
:::info 派生类包含了一个构造函数,它必须调用 super(),它会执行基类的构造函数,需要传递相应的参数。 而且,在构造函数里访问 this 的属性之前,我们一定要调用 super()。 这个是TypeScript强制执行的一条重要规则。 :::
修饰符
TypeScript 增加了修饰符,使其更像 C++、Java 等语言一般。
修饰符有:
- public 公有的
- private 私有
- protected 受保护的
- readonly 只读的(这个是额外的)
public
public 是指公有的,所有的成员访问不受到任何限制,类中默认所有成员都是 public,所以一般可以不写。
class Person {
public name: string;
public sex: string;
public constructor(name: string, sex: string) {
this.name = name;
this.sex = sex;
}
public eat() {
console.log(this.name + 'is eating');
}
}
private
private 修饰的属性不能在声明它的类外部访问,即不能在实例中直接访问,也不能在派生类中直接访问。只属于当前类,要在其他地方使用,只能通过调用当前类抛出的方法或者其他属性。
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
}
var person = new Person('aa')
person.name // 报错
// 继承类中也不能访问
class Man extends Person {
constructor(name: string) {
super(name)
}
eating() {
// console.log(this.name) // 报错
}
}
var man = new Man('bb')
man.name // 报错
:::warning TypeScript使用的是结构性类型系统。 当我们比较两种不同的类型时,并不在乎它们从何处而来,如果所有成员的类型都是兼容的,我们就认为它们的类型是兼容的。
然而,当我们比较带有 private 或 protected 成员的类型的时候,情况就不同了。 如果其中一个类型里包含一个 private 成员,那么只有当另外一个类型中也存在这样一个 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; // 错误: Animal 与 Employee 不兼容.
protected
protected 修饰的属性不能在当前类和派生类的实例中直接访问,但是在当前类和派生类内部可以直接访问。protected 是介于前两者之间的限制。
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
var person = new Person('aa')
person.name // 报错,当前类实例不能直接访问
// 继承类中可以访问
class Man extends Person {
constructor(name: string) {
super(name)
}
eating() {
console.log(this.name) // 成功
}
}
var man = new Man('bb')
man.name // 报错,继承类实例不能直接访问
构造函数标记为 protected,当前类不能创建实例,其派生类可以继承,也能够创建实例。可以应用于不实例化的基类
class Person {
name: string;
protected constructor(name: string) {
this.name = name;
}
}
class Man extends Person {
sex: string;
constructor(name:string, sex:string) {
super(name);
this.sex = sex;
}
}
var person = new Person('bb') // 报错
var man = new Man('bb', 'man')
readonly
readonly 修饰的属性是只读属性,初始化之后,再进行修改的时候,ts 会报错。readonly 修饰符可以和 public 等一起使用
class Person {
readonly name: string;
constructor(name: string) {
this.name = name
}
}
var person = new Person('xx')
person.name = 'asd' // 报错
参数属性
参数属性可以方便地让我们在一个地方定义并初始化一个成员。当一个成员带有修饰符时,可以直接将声明和初始化赋值合并到参数部分。此时参数名和属性名完全一致。
class Person {
constructor(readonly name: string='aa') {
// 直接合并声明和赋值的步骤到参数部分
}
}
var person = new Person()
console.log(person.name) // 存在 name
参数属性通过给构造函数参数前面添加一个访问限定符来声明,可以是 public、private、protected、readonly
// 如果没有修饰符,是不能合并的
class Person {
constructor(name: string='aa') {
}
}
var person = new Person()
console.log(person.name) // 报错,不存在 name
抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。
abstract class Person {
constructor(public name:string) {
}
work() {
console.log('working');
}
abstract eat(): void
}
var person = new Person('aa') // 报错,不能实例化抽象类
class Man extends Person {
eat() {
console.log('eating')
}
play() {
console.log('play')
}
}
var man8 = new Man('bb') // 默认是 调用的构造函数 类型
console.log(man.name)
man.eat()
man.work()
man.play() // 正确
// 对比
var man:Person = new Man('bb') // 可以指定为抽象类的类型
console.log(man.name)
man.eat()
man.work()
man.play() // 报错,如果指定了 man 的类型为抽象类,play 没有在抽象类中定义,则不能使用
:::info 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 abstract 关键字并且可以包含访问修饰符。 :::
类与接口的关系
类实例接口
在定义类时,通过 implements 关键字带一个接口,用于约束类的实例的公共属性和方法,类必须严格实现接口中的属性和方法,可多不可少。并且接口只会约束公有成员,如果将一个接口中的属性实现为私有成员,那么也会报错。
interface DogInterface {
eat(): void;
feet: number;
}
class Dog3 implements DogInterface {
eat() {
console.log('eat');
}
feet = 2;
}
// 下面对 eat 方法进行私有化,会报错
class Dog3 implements DogInterface {
private eat() {
console.log('eat');
}
feet = 2;
}
类构造函数接口
类构造函数接口主要用于将类作为参数传递时,声明参数的类型。这个接口用于约束类的构造器以及静态属性和方法。
interface DogConstructor {
new(num: number): DogInterface;
a: number;
run(): void;
}
interface DogInterface {
eat(): void;
feet: number;
}
class Dog3 implements DogInterface {
eat() {
console.log('eat');
}
feet = 2;
static a = 1;
static run() {}
}
function createDog(ctor: DogConstructor, num: number): DogInterface {
return new ctor(num);
}
let dog = createDog(Dog3, 1);