在现在的日常开发中,ts的使用是越来越常见,作为一个合格的前端人才,熟练的使用ts是面试和日常工作中必备的技能。但是最近在封装库的时候,发现自己虽然写了很久的ts,但是对类的使用和一些概念很模糊,用起来手生。既然如此那就不多bb了,赶紧学起来~~~
一. 编译
使用官网推荐的在线编译页面,叫做typescript playground(推荐使用这个)
页面包含中文版本还有各种配置项以及各种ts的版本。很适合学习使用。 之后默认的例子都在这里面使用。
二. 类的内部声明
2.1 类的属性声明
类的属性声明可以在类的顶部声明,也可以在constructor构造函数中声明。
2.1.1 顶部声明
如下,我们定义一个Person的类,类里面有name和age属性。
class Person {
name: string
age: number
}
如果我们这样写的话,在当前的ts版本中是会报错的,报错如下:
这个报错的大致意思就是,在声明的时候没有默认值。为什么会报这个错?这是因为在ts的配置文件中有一个选项 strictPropertyInitialization
默认是开启状态。
我们可以在页面的配置中,取消该项的选择。
(在项目中可以在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
}
}
这里注意两点
- constructor里面设置的属性,要提前在顶部声明。不然会找不到该属性
- 在顶部声明的属性设置值,在constructor里面也可以修改。
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
- 表示当前属性是公开的
- 可以在内部使用(类的本身)
- 可以在外部使用。(实例里面使用)
- 可以在子类中使用(可以继承使用)
protected
- 表示当前属性是受保护的
- 可以在内部使用(类本身)
- 外部无法访问。(实例中不可使用)
- 可以在子类中使用(可以继承)
private
- 表示当前的属性是私有的
- 可以在内部使用(类本身)
- 外部无法访问(实例中不可用)
- 不可在子类中使用(不可继承)
readonly
- 表示当前属性是只读属性
- 可以在内部使用,无法改值(类本身)
- 外部可以使用,无法改值
- 可以在子类中使用,无法改值
举个例子: 定义一个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() {
}
}
- 只要是外部接口中出现的属性,类中一定要满足类型。
- 外部接口没有出现的属性,类中无所谓,没有限制。
不仅仅是可以使用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中的类就先介绍到这里啦,**有喜欢的点个关注哈!!**