研究一下,typescript中类的使用

211 阅读7分钟

在现在的日常开发中,ts的使用是越来越常见,作为一个合格的前端人才,熟练的使用ts是面试和日常工作中必备的技能。但是最近在封装库的时候,发现自己虽然写了很久的ts,但是对类的使用和一些概念很模糊,用起来手生。既然如此那就不多bb了,赶紧学起来~~~

e9a7af06ec9baca4029bff92698524a6.jpeg

一. 编译

使用官网推荐的在线编译页面,叫做typescript playground(推荐使用这个)

image.png

页面包含中文版本还有各种配置项以及各种ts的版本。很适合学习使用。 之后默认的例子都在这里面使用。

二. 类的内部声明

2.1 类的属性声明

类的属性声明可以在类的顶部声明,也可以在constructor构造函数中声明。

2.1.1 顶部声明

如下,我们定义一个Person的类,类里面有name和age属性。

    class Person {
        name: string
        age: number
    }

如果我们这样写的话,在当前的ts版本中是会报错的,报错如下:

image.png

这个报错的大致意思就是,在声明的时候没有默认值。为什么会报这个错?这是因为在ts的配置文件中有一个选项 strictPropertyInitialization 默认是开启状态。

image.png

我们可以在页面的配置中,取消该项的选择。 (在项目中可以在tsconfig.json文件中修改该项配置)。但是不推荐这样做。

我们可以在声明的时候给个默认值:

    class Person {
        name: string = 'xiaomei'
        age: number = 20
    }

当然更高级一点的写法可以使用非空断言

    class Person {
        name!: string
    }

2.1.2 constructor中声明

我们可以对在顶部声明对属性,在constructor中进行初始化处理。

    class Person {
        name: string
        age: number
        constructor() {
            this.name = 'xiaomei'
            this.age = 20
        }
    }

这里注意两点

  1. constructor里面设置的属性,要提前在顶部声明。不然会找不到该属性
  2. 在顶部声明的属性设置值,在constructor里面也可以修改。

image.png

2.2 类的方法声明

类的方法声明跟普通函数一样,接受的参数以及返回值进行类型声明。

举个例子:在上面的类中增加几个方法

    enum Sex {
        MAN = 'man',
        WOMAN = 'woman'
    }

    class Person {
        name: string = 'xiao'
        age: number
        sex: Sex
        constructor() {
            this.name = 'xiaomei'
            this.age = 20
            this.sex = Sex.MAN
        }

        getName(): string {
            return this.name
        }

        setName(name: string): void {
            this.name = name
        }
    }

    let per = new Person()
    per.setName('xiaomeimei')
    console.log(per.getName()) // ‘xiaomeimei’

三. 类的修饰符

3.1 类的属性修饰符

简单例举一下常用的几种修饰符

public

  1. 表示当前属性是公开的
  2. 可以在内部使用(类的本身)
  3. 可以在外部使用。(实例里面使用)
  4. 可以在子类中使用(可以继承使用)

protected

  1. 表示当前属性是受保护的
  2. 可以在内部使用(类本身)
  3. 外部无法访问。(实例中不可使用)
  4. 可以在子类中使用(可以继承)

private

  1. 表示当前的属性是私有的
  2. 可以在内部使用(类本身)
  3. 外部无法访问(实例中不可用)
  4. 不可在子类中使用(不可继承)

readonly

  1. 表示当前属性是只读属性
  2. 可以在内部使用,无法改值(类本身)
  3. 外部可以使用,无法改值
  4. 可以在子类中使用,无法改值

举个例子: 定义一个A类,定义一个subA的子类继承A类

    class A {
        public name: string = 'aaa'
        protected age: number = 20
        private sex: string = 'man'
        readonly hibbit: string = 'changge'
        
        constructor() {
            this.hibbit = 'ball' // 在构造函数中,可以修改readonly属性
        }
    
        getName(): string {
            return this.name
        }
        getAge(): number {
            return this.age
        }
        getSex(): string {
            return this.sex
        }
    }

    class supA extends A {
        getSub() {
            console.log(this.name) // 在子类中public属性是可以访问的
            console.log(this.age) // 在子类中protected属性是可以访问的
            console.log(this.sex) // 子类中无法访问private   Property 'sex' is private and only accessible within class 'A'.
            console.log(this.hibbit) // 在子类中可以访问readonly属性
        }
    }

    let a = new A()
    console.log(a.getName())
    console.log(a.getAge())
    console.log(a.getSex())

    a.name = 'ccc'  // 在外部public属性是可以访问的
    a.age = 30.     // 在外部protected属性是无法访问   Property 'age' is protected and only accessible within class 'A' and its subclasses.
    a.sex = 'woman' // 在外部private属性是无法访问   Property 'sex' is private and only accessible within class 'A'.

    console.log(a.hibbit) // 在外部hibbit属性可以访问
    a.hibbit = 'foot' // readonly 属性不可修改 Cannot assign to 'hibbit' because it is a read-only property.(2540)

3.2 static修饰符

这个挺特殊的,单独拿出来讲一下。

在ts中,static用于定义类的静态成员。什么是静态成员?静态成员是属于 类的本身,而不属于类的实例,可以通过 类加属性/方法名 访问,但是在实例中不可访问。

如下,举个例子:

class S {
    static _name: string = 'xiao'
}

console.log(S._name) // 'xiao'

let s = new S()

s._name // Property '_name' does not exist on type 'S'. Did you mean to access the static member 'S._name' instead?

这个其实有很大作用,可以使用静态属性来存储全局大常量,或者定义一些静态的方法。

四. 类的接口

4.1 类的接口限制

类可以通过外部的interface或者type类型来限制一些属性或者方法的检查。使用 **implements**关键字,表示当前类满足外部类型条件的限制。

听起来有点拗口,其实很简单。就是外部定义一下属性和方法,在类中通过implements限制也要满足外部的条件。

如下例子:

interface IType {
    name: string,
    age: number
}

class MyPerson implements IType {
    name = 'xiaomei'
    age = '20' //报错  Property 'age' in type 'MyPerson' is not assignable to the same property in base type 'IType'.
             // Type 'string' is not assignable to type 'number'
    sex = ''
    constructor() {

    }

    getName() {

    }
}
  1. 只要是外部接口中出现的属性,类中一定要满足类型。
  2. 外部接口没有出现的属性,类中无所谓,没有限制。

不仅仅是可以使用interface或者type来限制类,类自己也可以作为外部类型条件限制类。简单来说,就是一个类的内部类型,限制别的类。

    class A {
    name: string = 'xiaomei'
    age: number = 20

    getName(): string {
        return this.name
    }
}

class B implements A {
    // B类中缺失A类中的getName方法
    // Class 'B' incorrectly implements class 'A'. Did you mean to extend 'A' and inherit its members as a subclass
    // Property 'getName' is missing in type 'B' but required in type 'A'
    name: string = 'x'
    age: string = '10' //  类型不可修改。Property 'age' in type 'B' is not assignable to the same property in base type 'A'.
                        // Type 'string' is not assignable to type 'number'.
    sex: string = 'man'. // 额外增加属性没有问题
}

在B类中一定要保证于A类的属性方法类型相同,可以多,但一定不能少。

4.2 多个类的限制

一个类可以通过多个接口限制类型。如下:

class A {
    name: string = 'xiaomei'
}

class B {
    age: number = 20
}

class C {
    sex: string = 'man'
}

class D implements A, B, C {
    name = ''
    age = 20
    sex = ''
}

D类通过implements同时要接受A,B,C三个类的接口限制。

五. Class类型

5.1 实例的类型

在ts中,类本身就是一种类型,但是它代表该类的实例类型,而不是 class 的自身类型。

class Person {
    name: string = 'xiaomei'
}

const p: Person = new Person()  // 可以用Person来声明实例的类型

如果当成类的类型来使用就会报错,如下:

class Person {
    name: string = 'xiaomei'
}

function setPerson(PersonClass: Person) {
    return new PersonClass(). // This expression is not constructable.Type 'Person' has no construct signatures
}

在上面的例子中,就是想把Person当成类的类型,但是报错,它只能用于声明实例的类型。

5.2 类的自身类型

如上面的例子,如何设置PersonClass的类型才不会报错?也就是如何设置类本身的类型。 我们可以使用关键字typeof

class Person {
    name: string = 'xiaomei'
}

function setPerson(PersonClass: typeof Person) {
    return new PersonClass()
}

这样写法是可以的。但是其实,在js中,构造函数的一种语法糖,我们完全可以写成构造函数的形式。new Function(其实从返回值中也可以看出来)具体如下:

class Person {
    name: string = 'xiaomei'
}

function setPerson(PersonClass: new () => Person) {
    return new PersonClass()
}

单独这样写,不是很好看,我们可以提出来,通过interface声明一下。

class Person {
    name: string = 'xiaomei'
}

interface IPerson {
    new (): Person
}

function setPerson(PersonClass: IPerson) {
    return new PersonClass()
}

这样看是不是感觉优雅了一点!

六. 类的继承

可以使用 extends 关键字继承另一个类的所有属性和方法。

class Person {
    name: string = 'xiaomei'
}

class subPerson extends Person {

}

let sb = new subPerson()
console.log(sb.name) // 'xiaomei'

在子类继承父类时,可以覆盖父类的同名方法。通过override实现.

class Person {
    name: string = 'xiaomei'
    getName() {
        console.log(2222)
    }
}

class subPerson extends Person {
    age: number = 20
    override getName() {
        console.log(11111)
    }
}
    

okk! ts中的类就先介绍到这里啦,**有喜欢的点个关注哈!!**