在复杂业务(如游戏开发、电商系统底层的购物车模型)中,我们往往需要把 “数据的属性” 和 “操作数据的方法” 封装在一起。这就需要使用传统的面向对象模型(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 访问修饰符” 一节中会详细介绍。
