TypeScript 类的定义

在复杂业务(如游戏开发、电商系统底层的购物车模型)中,我们往往需要把 “数据的属性” 和 “操作数据的方法” 封装在一起。这就需要使用传统的面向对象模型(OOP)中的 “类(Class)” 才行。

其中,类就像是一个 “模板”,通过这个模板创建出来的对象,我们称之为 “实例”。

TypeScript 类的基本语法

在 ES6 之前,JavaScript 只能使用 “非常别扭” 的原型链函数来模拟创建类。而在 TypeScript 中,我们可以直接使用 ES6提供的 class 关键字来定义类。

在 TypeScript 中定义一个类,通常包含 3 个核心部分:

  • 属性声明:这是 TypeScript 独有的要求,必须先声明类有哪些属性以及类型。
  • 构造函数(constructor):在使用 new 关键字创建对象时,会自动执行的初始化函数。
  • 方法:类中定义的普通函数,用于处理业务逻辑。

示例 1:定义一个 User 类

class User {
    // 1. 声明属性
    name: string;
    age: number;

    // 2. 定义构造函数
    constructor(userName: string, userAge: number) {
        this.name = userName;
        this.age = userAge;
    }

    // 3. 定义类的方法
    sayHello(): void {
        console.log(`姓名:${this.name},年龄:${this.age}`);
    }
}

// 实例化
const currentUser = new User("Jack", 20);

// 调用方法
currentUser.sayHello();

运行结果如下。

姓名:Jack,年龄:20

分析:

在原生 JavaScript 中,我们可以直接在 constructor() 里使用 this.name = userName 这种写法。但在 TypeScript 中,这种写法是绝对不被允许的。我们必须在类的最顶层,提前声明 name: string 和 age: number。

当我们敲下 new User("Jack", 20) 的一瞬间,TypeScript 编译器会在后台进行严格的双向校验:

  • 它会去检查 User 类的 constructor() 构造函数,发现你需要传入一个 string 和一个 number。
  • 如果你不传、少传、或者传错了类型,那么TypeScript 编辑器立马会报错拦截。

比如我们实例化对象时,传入的参数数量不匹配,此时 TypeScript 会直接报错。

const wrongUser = new User("Jack");
// 报错:应有 2 个参数,但获得 1 个。

TypeScript 类定义的注意事项

初学 TypeScript 类的小伙伴,最常遇到的一个报错就是 “属性没有初始化”。在开启了 TypeScript 严格模式的项目中,编译器对类的属性要求是非常苛刻的。

如果我们声明了一个属性,但既没有给它默认值,也没有在 constructor() 中给它赋值,此时 TypeScript 就会报错拦截。

示例 2:属性未初始化

class Article {
    title: string;
    author: string;

    constructor(title: string) {
        this.title = title;
    }
}

const myArticle = new Article("TypeScript 教程");
console.log(myArticle.author);

TypeScript 报错如下:

属性 “author” 没有初始化表达式,且未在构造函数中明确赋值

分析:

在实例化对象时,我们只给 title 属性传了值,并没有给 author 属性传值。此时 TypeScript 会拦截报错。

想要解决这个问题,我们可以使用下面 3 种方法:

// 方法 1:直接在声明时赋予默认值
author: string = "佚名";

// 方法 2:将其标记为可选属性(?)
readCount?: number;

// 方法 3:使用非空断言(!),强行告诉 TS 稍后一定会赋值
summary!: string;

提示: 在 Vue 3 与 TypeScript 结合的项目开发中,有很多属性(如组件的 DOM 引用、由依赖注入初始化的属性等)并不能在 constructor 中立刻拿到值。此时我们可以使用非空断言,就可以优雅地绕过 TypeScript 编译器的死板检查。

TypeScript 参数属性 “语法糖”

回顾一下我们在“示例 1”中写的代码,为了初始化 name 和 age,同一个单词我们足足重复写了 3 遍(声明一遍、参数里写一遍、this 赋值写一遍)。

如果一个类有十几个属性,这种 “样板代码” 会让人写到崩溃。为了解决这个问题,TypeScript 提供了一个极其强大的语法糖:参数属性(Parameter Properties)

我们只需要在 constructor 的参数前面加上 public 修饰符(后面会详细介绍),TypeScript 就会极其智能地在底层自动帮你完成 “声明 + 赋值” 的全套动作!

示例 3:使用语法糖定义类

class FastUser {
    // 直接在构造函数参数前加上 public,其他的统统省略
    constructor(public name: string, public age: number) {
        // 这里什么都不用写,TypeScript 已经自动帮你完成了 this.name = name 等操作
    }

    sayHello(): void {
        console.log(`我是 ${this.name},今年 ${this.age} 岁`);
    }
}

const user = new FastUser("Jack", 20);
user.sayHello();

运行结果如下。

我是 Jack,今年 20

分析:

是不是感觉清爽多了?这种语法糖在现代企业级开发中被极其广泛地使用。至于刚才提到的 public 到底是什么?它还有哪些兄弟姐妹?我们在后面 “TypeScript 访问修饰符” 一节中会详细介绍。

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号