TypeScript 的类(Classes)是面向对象编程的核心组成部分,提供了对类的支持,包括继承、访问修饰符、构造函数、静态成员等。通过 TypeScript 类,你可以创建结构良好且类型安全的对象。以下是一些关于 TypeScript 类的关键概念和用法示例。
定义一个简单的类
typescript">/*** Greeter 类用于生成带有特定问候语的欢迎消息。*/
class Greeter {greeting: string;/*** 构造函数,用于创建 Greeter 类的新实例。* * @param message - 传递给构造函数的问候消息,用于设置 greeting 属性。*/constructor(message: string) {this.greeting = message;}/*** 生成欢迎消息。* * @returns 返回包含欢迎消息的字符串。*/greet() {return "Hello, " + this.greeting;}
}// 创建 Greeter 类的一个实例,传入问候消息 "world"
let greeter = new Greeter("world");
// 调用 greeter 实例的 greet 方法,并将欢迎消息输出到控制台
console.log(greeter.greet()); // 输出 "Hello, world"
在这个例子中:
greeting
是一个类的属性。constructor
是构造函数,用于初始化新创建的对象。greet
是一个方法,返回一个问候信息。
访问修饰符
TypeScript 提供了三种访问修饰符来控制类成员的可见性:
public
:默认值,成员可以在任何地方被访问。private
:成员只能在定义它们的类内部访问。protected
:成员可以在其自身类以及派生类中访问。
访问修饰符是 TypeScript 类中用来控制类成员(属性和方法)可见性和可访问性的关键字。TypeScript 提供了三种访问修饰符:public
、private
和 protected
。下面通过一个具体的例子来展示这些访问修饰符的使用。
示例:使用访问修饰符
typescript">class Employee {// 默认为 public,可以在任何地方访问public department: string;// private 成员只能在定义它的类内部访问private salary: number;// protected 成员可以在其自身类以及派生类中访问protected id: number;constructor(department: string, salary: number, id: number) {this.department = department;this.salary = salary;this.id = id;}// 公共方法,可以公开访问public getDetails(): void {console.log(`Employee in ${this.department}, ID: ${this.id}`);this.printSalary(); // 私有方法可以在类内调用}// 私有方法,只能在类内部调用private printSalary(): void {console.log(`Salary: ${this.salary}`);}
}// 创建 Employee 类的实例
let emp = new Employee("Engineering", 70000, 12345);// 可以访问公共成员
console.log(emp.department); // 输出 "Engineering"// 不能直接访问私有成员或方法
// console.log(emp.salary); // Error: Property 'salary' is private and only accessible within class 'Employee'.
// emp.printSalary(); // Error: Method 'printSalary' is private and only accessible within class 'Employee'.// 可以访问保护成员,但在外部无法直接访问
// console.log(emp.id); // Error: Property 'id' is protected and only accessible within class 'Employee' and its subclasses.
在这个例子中:
department
是一个public
属性,默认情况下所有类成员都是public
的,这意味着它们可以在任何地方被访问。salary
是一个private
属性,它只能在Employee
类的内部访问。尝试从外部访问会导致编译错误。id
是一个protected
属性,它不仅可以在Employee
类内部访问,也可以在其派生类中访问,但在类外部仍然不可见。getDetails
是一个public
方法,因此可以从外部调用。printSalary
是一个private
方法,只能在Employee
类的内部调用。
输出结果:
Engineering
Employee in Engineering, ID: 12345
Salary: 70000
扩展:继承与保护成员
为了进一步说明 protected
访问修饰符的作用,我们可以创建一个继承自 Employee
的子类,并查看如何访问 protected
成员。
typescript">class Manager extends Employee {constructor(department: string, salary: number, id: number, public reports: number) {super(department, salary, id);}// 子类可以访问父类中的 protected 成员showId(): void {console.log(`Manager ID: ${this.id}`); // OK, because 'id' is protected}
}let manager = new Manager("Management", 90000, 67890, 5);manager.getDetails(); // 调用继承的方法
manager.showId(); // 调用子类中的方法,可以访问 protected 成员
在这个扩展的例子中:
Manager
类继承自Employee
,并且能够访问protected
的id
属性。showId
方法展示了如何在子类中合法地访问protected
成员。
通过这个例子,你可以看到不同访问修饰符对类成员访问权限的影响,这有助于构建更加模块化和安全的代码结构。合理使用访问修饰符可以确保你的类设计既灵活又安全。
继承
子类可以继承父类的属性和方法,并可以重写或扩展这些成员。
typescript">/*** 定义一个Person类,用于表示一个人的基本信息*/
class Person {protected name: string;/*** 构造函数,初始化人的姓名* @param name 人的姓名*/constructor(name: string) {this.name = name;}/*** 说你好的方法* @returns 返回一个打招呼的字符串*/sayHello() {return `Hello, my name is ${this.name}`;}
}/*** 定义一个Employee类,继承自Person类,用于表示一个员工的信息*/
class Employee extends Person {private department: string;/*** 构造函数,初始化员工的姓名和部门* @param name 员工的姓名* @param department 员工所属的部门*/constructor(name: string, department: string) {super(name); // 调用父类的构造函数this.department = department;}/*** 获取电梯演讲的内容* @returns 返回一个包含打招呼和工作部门信息的字符串*/getElevatorPitch() {return `${this.sayHello()} and I work in ${this.department}.`;}
}// 创建一个Employee实例
let howard = new Employee("Howard", "Sales");
// 输出电梯演讲的内容
console.log(howard.getElevatorPitch()); // 输出 "Hello, my name is Howard and I work in Sales."
静态成员
你可以使用 static
关键字来定义属于类而不是实例的属性和方法。
typescript">// 定义一个Grid类,用于在二维平面上进行距离计算
class Grid {// 静态属性origin代表坐标原点static origin = { x: 0, y: 0 };/*** 计算给定点到原点的距离* @param point 一个包含x和y坐标的对象,表示要计算距离的点* @returns 返回计算出的距离,根据实例化的scale比例进行调整*/calculateDistanceFromOrigin(point: { x: number; y: number }) {let xDist = point.x - Grid.origin.x;let yDist = point.y - Grid.origin.y;return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;}// 构造函数,接受一个scale参数,用于距离计算时的比例调整constructor(public scale: number) {}
}// 创建一个比例为1的Grid实例
let grid1 = new Grid(1.0); // 1x scale
// 创建一个比例为5的Grid实例
let grid2 = new Grid(5.0); // 5x scale// 使用grid1实例计算点(10, 10)到原点的距离并打印
console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 })); // 输出 14.142135623730951
// 打印Grid类的静态属性origin的x坐标
console.log(Grid.origin.x); // 输出 0
抽象类
抽象类不能直接实例化,通常作为基类来定义一组接口或实现某些通用功能。
typescript">/*** 抽象类Department用于定义一个部门的基本属性和方法*/
abstract class Department {/*** 构造函数,初始化部门名称* @param name 部门的名称*/constructor(public name: string) {}/*** 打印部门名称的方法*/printName(): void {console.log('Department name: ' + this.name);}/*** 抽象方法,用于打印会议信息* 必须在派生类中实现*/abstract printMeeting(): void;
}/*** AccountingDepartment类继承自Department类* 用于定义会计部门的具体行为*/
class AccountingDepartment extends Department {/*** 实现了抽象方法printMeeting,具体打印会计部门的会议信息*/printMeeting(): void {console.log('The Accounting Department meets each Monday at 10am.');}/*** 生成会计报告的方法*/generateReports(): void {console.log('Generating accounting reports...');}
}// 声明一个Department类型的变量
let department: Department;// department = new Department(); // 错误: 抽象类不能实例化
// 实例化AccountingDepartment对象
department = new AccountingDepartment("Accounting");// 调用部门的打印名称方法
department.printName();
// 调用部门的打印会议信息方法
department.printMeeting();
获取器与设置器(Getters & Setters)
TypeScript 支持 JavaScript 的 getter 和 setter 方法,允许你更细粒度地控制属性的读取和赋值操作。
typescript">/*** 定义一个员工类*/
class Employee {// 员工全名的私有属性private _fullName: string;/*** 获取员工全名* @returns {string} 员工的全名*/get fullName(): string {return this._fullName;}/*** 设置员工全名* @param {string} newName 新的全名* @throws {Error} 如果名字长度小于等于3,则抛出错误*/set fullName(newName: string) {if (newName && newName.length > 3) {this._fullName = newName;} else {throw new Error("Invalid name");}}
}// 创建员工实例
let employee = new Employee();
// 设置员工全名,这里是一个有效的名字
employee.fullName = "Bob Smith";
// 输出员工全名
console.log(employee.fullName); // 输出 "Bob Smith"// 尝试设置一个无效的名字,这会抛出错误
// employee.fullName = "John"; // 这会抛出错误,因为名字长度不足
总结
- 定义类:使用
class
关键字。 - 访问修饰符:
public
、private
和protected
控制成员的可见性。 - 继承:使用
extends
关键字从现有类派生新类。 - 静态成员:使用
static
关键字定义不属于实例的成员。 - 抽象类:定义不能直接实例化的基类。
- 获取器与设置器:提供对属性的受控访问。
通过理解和应用这些特性,你可以利用 TypeScript 的强大功能构建复杂且类型安全的应用程序。类是组织代码的重要工具,帮助你创建可复用、易于维护和扩展的组件。