TypeScript面向对象

发布时间 2023-07-06 22:53:54作者: wdszh

TypeScript面向对象

面向对象是程序中一个非常重要的思想。面向对象很简单,简而言之就是程序之中所有的操作都需要通过对象来完成。一切皆对象

接口

TypeScript中的接口跟传统语言(比如Java)的接口有点差别

对象可以隐式实现接口

概念

描述一个类型

一个接口里面可以有:

  • 字段
  • 方法
  • 可选字段以及方法
  • 只读字段

用法

  • 可选字段串联
interface Employee{
    name?:{
        first?:string
        last: string
    }
    // 只读字段
    readonly addr: string
}


function hasBadName(emp: Employee){
    // 可选字段使用?来串联,防止报错
    return emp.name?.first?.startsWith("A")
}
  • 非空断言
function hasBadName(emp: Employee){
    // 使用!来串联字段,表示为非空,但是如果真的是空就会报错
    return emp.name!.first!.startsWith("A")
}
  • 接口的扩展
interface HasName{
       name?:{
        first?:string
        last: string
    } 
}

// 使用extends关键字类扩展接口
interface Employee extends HasName{
    salary:number
    bonus?:number
}
  • 类型的并
  • 类型断言
  • 类型判断
// 定义2个接口
interface MyButton{
    visible: boolean
    enabled: boolean
    onClick(): void
}

interface MyImage{
    visible: boolean
    src: string
    width: number
    height: number
}

// 方法参数是 类型的并
function processElement(e: MyButton | MyImage){
    // 类型判断
    if((e as any).onClick){
        // 类型断言
        const btn = e as MyButton
        btn.onClick()
    }else{
        const img = e as MyImage
        console.log(img.src)
    }
}

// 我们还可以简化上面的方法
// 通过这样定义一个方法来判断是否是某接口类型, 返回值类型是boolean, 可以写成e is MyButton 
// 这种语法有点奇怪,但是也好理解,就是我们的返回结果是否是MyButton类型
function isButton(e: MyButton | MyImage): e is MyButton{
    return (e as MyButton).onClick !== undefined
}

// 简化上面的方法
function processElement2(e: MyButton | MyImage){
    // 类型判断,由于上面的e is MyButton的语法,这里就不需要 as 的这种语法了:const btn = e as MyButton
    if(isButton(e)){
        e.onClick()
    }else{
        console.log(e.src)
    }
}

接口实现

  • 对象可以隐式实现接口

  • 类的定义
  • 构造函数
  • private/public的属性
  • 方法
  • getter/setter方法
  • 类的继承

用法如下

class Employee {
    private _bonus?: number

    // 写在对象里面的属性
    addr?: string
    // 通过构造器的参数来描述属性
    // 在对象里面写的属性或者构造器的属性都可以说明访问权限 public或private
    constructor(public name: string, private salary: number){}

    // setter/getter 方法
    set bonus(v:number){
        this._bonus = v
    }
    get bonus(){
        return this._bonus || 0
    }

    // 对象里面的方法
    updateSalary(){
        // do something
    }
}


// 类的继承
class Manager extends Employee {
    private reporters: Employee[] = []

    constructor(name: string, salary: number){
        super(name, salary)
    }

    addReporter(emp: Employee){
        this.reporters.push(emp)
    }

}

// 使用类
const emp = new Employee("a", 100)
emp.addr = "北京"

emp.bonus = 10000

console.log(emp)

const manager = new Manager("b", 200)

console.log(manager)

使用类来实现接口

  • 隐式实现

TypeScript中隐式实现接口,接口与实现类之间的耦合非常低

TypeScript是通过对比类与接口的属性,方法,全部包含这个接口中的就实现了这接口

// 定义一个接口
interface IEmp{
    name:string
    salary:number
    bonus?:number
    addr?:string
}

// 那么上面的Employee、Manager 就实现了这个接口
const emp: IEmp = new Employee("a", 100)

const manager: IEmp = new Manager("b", 200)
  • 显示实现接口
class EmployeeImpl implements IEmp {
// ...
}
  • 定义者=使用者 隐式实现 (推荐)go语言也是这么做的
  • 定义者=实现者 显示实现 传统的编程语言都是这样,比如Java
  • 在实际中接口应该是使用者来定义才更符合我们的习惯,因为使用者才知道我需要接口给我提供什么能力

泛型

主要用来约束方法参数,类似java的泛型

基本使用

// 泛型约束数组中的元素类型
const arr: Array<number> = []

arr.push(1)

// 泛型约束Promise的返回值类型
new Promise<string>((resolve, reject) => {
    return resolve("ok")
})

使用泛型定义方法与对象

class MyArray<T> {
    data: T[] = []
    add(t: T) {
        this.data.push(t)
    }

    map<U>(fun: (t: T) => U): U[] {
        return this.data.map(fun)
    }
    print() {
        console.log(this.data)
    }
}

泛型的限定

interface HasName {
    name: string
}

// 限定泛型必须实现此接口,泛型元素就可以使用此接口的元素或者方法了
class MyArray<T extends HasName> {
    data: T[] = []
    add(t: T) {
        this.data.push(t)
    }

    map<U>(fun: (t: T) => U): U[] {
        return this.data.map(fun)
    }

    getAllName(): string[] {
        return this.data.map(v => v.name)
    }

    print() {
        console.log(this.data)
    }
}