TS系列教程十:类

75 阅读3分钟

定义

类是面向对象编程的基石,因为vue和react的缘故,工作很少用,一般大量用于框架,ts也对类做了很好的支持


class Person{
    name:string;//只定义类型
    name='Tom';//直接赋值
    constructor(name:string){
        this.name=name;
    }
}

可以在属性声明的时候直接赋值,也可以在构造函数中赋值,以构造函数里边的赋值为最后赋值

class Person{
    name:string;//只定义类型
    constructor(name='Tom'){
        this.name=name;
    }
}

也可以在构造函数中定义默认值

class Person{
    name:string;//只定义类型
    constructor(name='Tom'){
        this.name=name;
    }
    sayHello(){
        console.log(`hello!${this.name}`)
    }
}

定义函数方法也是一样的

只读readonly

class Person{
    readonly name:string;//只定义类型
    constructor(name='Tom'){
        this.name=name;
    }
}
const person=new Person()
person.name='Jack'//报错因为它是只读属性

只读属性只能在定义或者构造函数中赋值

函数的重载

class Person{
    readonly name:string='Tom';//只定义类型
    readonly age:number=18;
    readonly sex:number=1;
    constructor(name:string,sex?:number)
    constructor(age:number)
    constructor(nameOrAge:string|number,sex?:number){
        if(typeof nameOrAge === "string"){
            this.name=nameOrAge;
            this.sex=sex as number;
        }else{
            this.age=nameOrAge;
        }
    }
}

const p1=new Person("Jerry",1);
console.log(p1.name);


const p2=new Person(20);
console.log(p2.age);

构造函数的重载格式就是上边这样,可以传入一个参数也可以传入多个,这里有个问题是必须给属性一个默认值 否则就报错,另外第二个参数要写成可选参数,并且赋值的时候要使用as,因为sex定义的是number,传入的时候因为是可选属性所以它的类型是number|undefined,直接赋值会报错的

class Person{
    readonly name:string='Tom';//只定义类型
    constructor(name:string):string{//报错
       this.name=name;
    }
}

不能给构造函数手动设置返回值,因为它总是返回实例也就是this

类的get和set

class Circle {
    #radius;  // 私有属性
    constructor(radius: number) {
        this.#radius = radius; // 使用下划线来表示私有属性
    }

    get radius() {
        return this.#radius;  // 定义获取半径的行为
    }

    set radius(newRadius) {
        if (newRadius <= 0) {
            throw new Error("半径必须大于0");
        }
        this.#radius = newRadius;  // 定义设置半径的行为
    }

    get area() {
        return Math.PI * this.#radius ** 2;  // 定义获取面积的行为
    }
}

const c = new Circle(5);
console.log(c.radius); // 输出: 5
console.log(c.area);   // 输出: 78.54

c.radius = 3;
console.log(c.radius); // 输出: 3
console.log(c.area);   // 输出: 28.27

c.radius = -1; // 报错: Uncaught Error: 半径必须大于0

是不是有pinia和计算属性的感觉,其实用函数也能实现这些功能,但是用get和set更加符合面向对象的思想,维护性也更高,说白了就是逼格更高

属性索引

class Person{
    [property:string]:string|((s:0|1)=>boolean);
    #name='张三'
    isBoy(sex:0|1){
        console.log('boy name is:',this.#name);
        return Boolean(sex);
    }
    sayName(){//报错  类型“() => string”的属性“sayName”不能赋给“string”索引类型“string | ((s: 0 | 1) => boolean)”
        return 'boy name is:'+this.#name
    }
}

这样定义代表这个类只包含类型为string的属性或返回值为布尔的方法,不能包含其它的。

implements 的使用

interface Person{
    name: string;
    age: number;
}
type Person={
    name: string;
    age: number;
}
class PersonClass implements Person{
    name="John";
    age=30;    
}

type或interface定义都可以,然后类名称后用implements,另外需要注意的是implements并不是像type和interface那样,他只是检测下这个类是不是满足你定义的类型,下边这个例子解释下我想说的

interface Person{
    name: string;
    age?: number;
}
const person1: Person={name:'Tome'};
person1.age=19;

class PersonClass implements Person{
    name="John";
}
const person = new PersonClass();
person.age = 25;//报错 提示实例上没有age属性

可以对比下差别。
implements后边还可以加另一个类

class Car{
    name='BMW';
}
class PersonClass implements Car{
    name="John";//不能省略
}

类与接口的合并

class Car{
    name='BMW';
}
interface Car{
    color:string;
}
let myCar=new Car();
myCar.color='red';

当类和接口同名时,会产生合并,这也算ts的一大特性吧。但是别名 type不行 会报错的

未完待续...

因类后边要注意的东西过于复杂且应用场景较少,后边的教程以后再更新吧