一、类的基本使用
1.定义
class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}greet() {return "Hello, " + this.greeting;}}let greeter = new Greeter("world");
这个类有三个成员:一个greeting属性,一个构造函数,一个greet方法,我们通过this访问类的成员。
最后通过new构造了一个类的实例。
这个代码编译成 js 代码就是如下:
var Greeter = /** @class */ (function () {function Greeter(message) {this.greeting = message;}Greeter.prototype.greet = function () {return "Hello, " + this.greeting;};return Greeter;}());var greeter = new Greeter("world");
2.继承
在 Typescript 中使用extends关键字来定义类的继承:
class Father{fatherName: string = 'fatherName';name: string; // 自己、自己的子类、自己的孙子类都能访问age: number; // 自己和自己的子类能访问,其他类不能访问money: number; // 只有自己能访问,子类和其他类不能访问constructor(name: string, age: number, money: number){this.name = namethis.age = agethis.money = money}getName():string{return this.name}toString(){console.log('Father')}}class Child extends Father{childName: string = 'childName';constructor(name: string, age: number, money: number){super(name, age, money)}desc(){console.log(this.name, this.age)}toString(){ // 子类和父类有重名方法时,子类会覆盖父类console.log('Child')}}let child = new Child('yf', 10, 11)console.log(child.name)
Child 通过 extends 继承了 Father,并且在Child 的constructor中调用了super()方法,他会执行基类的构造函数,而且是在访问this的属性之前调用 super() 方法,这是Typescript 继承的关键,这样Child的实例就会继承Father 的所有能够被继承的属性和方法(有些类的修饰符修饰的属性不能被继承,我们后面会说到)。
二、类的修饰符
1.公共,私有与受保护的修饰符
(1)public
通过public 修饰符指定的成员是在任何地方都可见的,也就说他自己、自己的子类、自己的孙子类都能访问到,在Typescript 中 成员都是默认为 public 的。
class Father{public name: string; // 自己、自己的子类、自己的孙子类都能访问constructor(name: string, age: number, money: number){this.name = name}getName():string{return this.name}toString(){console.log('Father')}}class Child extends Father{constructor(name: string, age: number, money: number){super(name)}}let child = new Child('yf')console.log(child.name) // yf
上面定义的public类通过子类继承之后,子类的实例也能访问到。
(2)private
private 私有的,他修饰的成员只能由他自己访问,子类和其他类都不能访问。
class Father{public name: string; // 自己、自己的子类、自己的孙子类都能访问private money: number; // 只有自己能访问,子类和其他类不能访问constructor(name: string, money: number){this.name = namethis.money = money}getName():string{return this.name}toString(){console.log('Father')}}const father = new Father('leah', 10);console.log(father.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)class Child extends Father{constructor(name: string, money: number){super(name, money)}desc(){console.log(this.name, this.money) // Property 'money' is private and only accessible within class 'Father'.console.log(this.name)}toString(){ // 子类和父类有重名方法时,子类会覆盖父类console.log('Child')}}let child = new Child('yf', 11)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 修饰的成员只能被自己和自己的子类访问,其他类不能访问。
class Father{static fatherName: string = 'fatherName';public name: string; // 自己、自己的子类、自己的孙子类都能访问protected age: number; // 自己和自己的子类能访问,其他类不能访问private money: number; // 只有自己能访问,子类和其他类不能访问constructor(name: string, age: number, money: number){this.name = namethis.age = agethis.money = money}getName():string{return this.name}toString(){console.log('Father')}}const father = new Father('leah', 20, 10);console.log(father.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)console.log(father.age) // Property 'age' is protected and only accessible within class 'Father' and its subclasses.ts(2445)class Child extends Father{static childName: string = 'childName';constructor(name: string, age: number, money: number){super(name, age, money)}desc(){// console.log(this.name, this.age, this.money) // Property 'money' is private and only accessible within class 'Father'.console.log(this.name, this.age)}toString(){ // 子类和父类有重名方法时,子类会覆盖父类console.log('Child')}}Child.fatherName; // 子类继承了父类的静态方法Child.childName;let child = new Child('yf', 10, 11)console.log(child.money) // Property 'money' is private and only accessible within class 'Father'.ts(2341)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只能被子类继承,不能用来创建实例。
class Test {protected constructor(){}}const t = new Test() // Constructor of class 'Test' is protected and only accessible within the class declaration.ts(2674)class ChildTest extends Test{constructor(){super()}}const Ct = new ChildTest()
2.readonly
readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Father{static fatherName: string = 'fatherName';public name: string; // 自己、自己的子类、自己的孙子类都能访问protected age: number; // 自己和自己的子类能访问,其他类不能访问private money: number; // 只有自己能访问,子类和其他类不能访问readonly hight: number;constructor(name: string, age: number, money: number, hight: number){this.name = namethis.age = agethis.money = moneythis.hight = hight}getName():string{return this.name}toString(){console.log('Father')}}const father = new Father('leah', 20, 10, 150);
三、类的属性
1.参数属性
在上面我们先是通过修饰符初始化了一个属性,然后在构造函数里用接收的参数对属性进行赋值,这个过程可以用参数属性进行简化,就是在构造函数的参数前面加上访问修饰符。
class Father{constructor(name: string, protected age: number, private money: number, readonly hight: number){this.name = namethis.age = agethis.money = moneythis.hight = hight}getName():string{return this.name}toString(){console.log('Father')}}
2.静态属性
静态属性通过 static 关键字来指定,被指定的这个成员不会被实例继承,而是直接通过类来调用.
class Father{static fatherName: string = 'fatherName';constructor(name: string, protected age: number, private money: number, readonly height: number){this.name = namethis.age = agethis.money = moneythis.height = height}}const father = new Father('leah', 20, 10, 150);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)console.log(Father.fatherName)
父类的静态属性/方法,可以被子类继承
class Father{static fatherName: string = 'fatherName';constructor(name: string, protected age: number, private money: number, readonly height: number){this.name = namethis.age = agethis.money = moneythis.height = height}}const father = new Father('leah', 20, 10, 150);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)console.log(Father.fatherName)class Child extends Father{static childName: string = 'childName';constructor(name: string, age: number, money: number, height: number){super(name, age, money, height)}}console.log(Child.fatherName); // 子类继承了父类的静态方法console.log(Child.childName);let child = new Child('yf', 10, 11, 150)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.可选属性
可选属性表示可以传也可以不穿参数。
class Test {name?: string;constructor(name?: string){this.name = name}}const test1 = new Test()const test2 = new Test('leah')
四、存取器
通过 getters/setters 来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
class UserInfo {name: string = '';constructor(){}get getName(){return this.name}set setName(name: string){this.name = name}}
五、抽象类
抽象类一般用来被其他类继承使用,而不是直接实例化使用。
使用abstract 关键字定义抽象类:
abstract class People{constructor(public name: string){}abstract getName(): string}class Student extends People{constructor(name: string){super(name)this.name = name}getName(){return this.name}}const student = new Student('leah')student.getName()const people = new People() // Cannot create an instance of an abstract class.ts(2511)
在 People 类里面使用abstract 定义了一个抽象方法 getName,并且指定了这个方法的返回类型,也可以定义方法的参数类型。然后在 派生类 Student 里面实现了这个抽象方法,最后再通过Student 的实例调用这个抽象方法。
当对抽象类 People 进行实例化的时候报错了,所以抽象只能被子类继承了,实例化之后才能访问。
注意,必须是在派生类中实现了这个抽象方法的细节之后才能访问,不让不能访问。
abstract class People{constructor(public name: string){}abstract getName(): string}class Student extends People{ // Non-abstract class 'Student' does not implement inherited abstract member 'getName' from class 'People'.ts(2515)constructor(name: string){super(name)this.name = name}// getName(){// return this.name// }}const student = new Student('leah')student.getName()
在定义Student 类的时候报错了,抽象类里定义的抽象方法不会被子类继承,必须在在子类中实现该方法的定义。
/*** 获取站点信息*/protected getSiteInfo(): SiteInfo{return new SiteInfo({appId: this.config.appId,appVersion: this.config.appVersion,version,platform: this.platformTye,uid: this.globalUUID,sid: this.globalSessionId,userId: this.config.userId,ext: this.config.ext,device: this.deviceInfo,pageInfo: this.getPageInfo()})}/*** 获取页面信息*/protected getPageInfo(): PageInfo
在父类中定义了getPageInfo函数,我们不希望父组件去实现这个函数,而是由子组件实现,此时这种写法会报错,Function implementation is missing or not immediately following the declaration. 意思是 这个函数并没有被实现,要想不报错,只需要加个 abstract 关键字。
/*** 获取页面信息*/protected abstract getPageInfo(): PageInfo
六、类与接口
1.类继承接口
interface Animal{name: string}class Dog implements Animal{// name: string // 可以这样声明也可以在构造函数中声明。constructor(public name: string){}}
定义一个Animal 接口,这个接口有一个 name 属性,定义的类 Dog 要继承这个接口,类继承接口必须使用 implements 关键字。(如果是类继承类、接口继承接口就直接使用extends 关键字)。类实现接口之后,必须声明接口中定义的属性以及方法。
一个类可以继承多个接口,且必须要将所有接口的所有属性和方法都实现了。
interface Animal{name: string}interface Carnivore{type: string}class Tiger implements Animal, Carnivore{// name: string// type: stringconstructor(public name:string, public type:string){}}
2.接口继承类
class Animal{name: string;}interface A extends Animal{}class B implements A{} // Property 'name' is missing in type 'B' but required in type 'Animal'.ts(2420)class C implements A{ // 必须要将接口中包含的所有属性和方法都声明了才可以,不然编译会报错name: string}
七、在泛型中使用类
const create = <T>(c: { new (): T }): T => {return new c();};class Info {age: number;}create(Info).age;create(Info).name; // error 类型“Info”上不存在属性“name”
创建了一个create函数,传入的参数是一个类,返回的是一个类创建的实例
参数c的类型定义中,new() 代表调用类的构造函数,他的类型也就是类创建的实例的类型
return new c() 表示使用传进来的 类 c 创建一个实例并返回。
