TypeScript 为 JavaScript 的 class 添加了类型注解和访问控制能力。
4.1 基本类定义
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `我是 ${this.name},今年 ${this.age} 岁`;
}
}
const p = new Person("张三", 25);
console.log(p.greet()); // "我是 张三,今年 25 岁"
简写形式(参数属性)
在构造函数参数前加修饰符,可以自动创建并赋值属性:
class Person {
// 等同于声明属性 + 在 constructor 中赋值
constructor(
public name: string,
public age: number,
) {}
greet(): string {
return `我是 ${this.name},今年 ${this.age} 岁`;
}
}
💡 这是 TS 独有的语法糖,可以大幅减少样板代码。
4.2 访问修饰符
| 修饰符 | 类内部 | 子类 | 类外部 |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
class BankAccount {
public owner: string; // 谁都能访问(默认)
protected balance: number; // 子类可以访问
private password: string; // 只有自己能访问
constructor(owner: string, balance: number, password: string) {
this.owner = owner;
this.balance = balance;
this.password = password;
}
public getBalance(): number {
return this.balance;
}
private verify(pwd: string): boolean {
return pwd === this.password;
}
}
const account = new BankAccount("张三", 1000, "123456");
account.owner; // ✅ public
account.balance; // ❌ protected
account.password; // ❌ private
account.getBalance(); // ✅ 通过公开方法访问
JS 原生私有字段 #
class Secret {
#value: string; // JS 原生私有,运行时也无法访问
constructor(value: string) {
this.#value = value;
}
getValue(): string {
return this.#value;
}
}
💡
private只在编译时检查,#在运行时也生效。推荐使用#来实现真正的私有。
4.3 只读属性
class Config {
readonly apiUrl: string;
constructor(url: string) {
this.apiUrl = url; // ✅ 构造函数中可以赋值
}
update() {
this.apiUrl = "new"; // ❌ 只读,不能修改
}
}
4.4 继承
class Animal {
constructor(public name: string) {}
move(distance: number): void {
console.log(`${this.name} 移动了 ${distance} 米`);
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // 必须调用 super()
}
bark(): void {
console.log("汪汪!");
}
// 重写父类方法
move(distance: number): void {
console.log("🐕 跑起来了!");
super.move(distance); // 调用父类方法
}
}
const dog = new Dog("旺财", "柴犬");
dog.bark(); // "汪汪!"
dog.move(10); // "🐕 跑起来了!" → "旺财 移动了 10 米"
4.5 抽象类
抽象类不能被实例化,只能被继承。它可以包含抽象方法(没有实现的方法):
abstract class Shape {
abstract area(): number; // 抽象方法:子类必须实现
abstract perimeter(): number;
// 普通方法:子类可以直接使用
describe(): string {
return `面积: ${this.area()}, 周长: ${this.perimeter()}`;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
perimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(
private width: number,
private height: number,
) {
super();
}
area(): number {
return this.width * this.height;
}
perimeter(): number {
return 2 * (this.width + this.height);
}
}
// const shape = new Shape(); // ❌ 不能实例化抽象类
const circle = new Circle(5);
console.log(circle.describe()); // "面积: 78.54, 周长: 31.42"
4.6 实现接口(implements)
类可以通过 implements 来遵循接口的约定:
interface Printable {
print(): void;
}
interface Loggable {
log(message: string): void;
}
// 实现多个接口
class Report implements Printable, Loggable {
constructor(private title: string) {}
print(): void {
console.log(`📄 ${this.title}`);
}
log(message: string): void {
console.log(`[Report] ${message}`);
}
}
interface vs abstract class
| 特性 | interface | abstract class |
|---|---|---|
| 多重实现/继承 | ✅ 一个类可实现多个接口 | ❌ 只能继承一个类 |
| 有默认实现 | ❌ | ✅ 可以有普通方法 |
| 有构造函数 | ❌ | ✅ |
| 运行时存在 | ❌ 编译后消失 | ✅ |
4.7 静态成员
class MathHelper {
static PI = 3.14159;
static square(n: number): number {
return n * n;
}
static cube(n: number): number {
return n ** 3;
}
}
// 直接通过类名调用,不需要实例化
MathHelper.PI; // 3.14159
MathHelper.square(5); // 25
4.8 Getter 和 Setter
class Temperature {
private _celsius: number = 0;
get celsius(): number {
return this._celsius;
}
set celsius(value: number) {
if (value < -273.15) {
throw new Error("温度不能低于绝对零度");
}
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 1.8 + 32;
}
set fahrenheit(value: number) {
this.celsius = (value - 32) / 1.8;
}
}
const temp = new Temperature();
temp.celsius = 100;
console.log(temp.fahrenheit); // 212
temp.celsius = -300; // ❌ 抛出错误
4.9 类作为类型
在 TypeScript 中,类本身就是一个类型:
class Point {
constructor(public x: number, public y: number) {}
}
// Point 既是构造函数,也是类型
function distance(a: Point, b: Point): number {
return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
}
// 结构类型:只要形状匹配就行
const p1 = new Point(0, 0);
const p2 = { x: 3, y: 4 }; // 不是 Point 实例,但结构匹配
distance(p1, p2); // ✅ TypeScript 使用结构类型
4.10 单例模式
class Database {
private static instance: Database;
private constructor(private connectionString: string) {}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database("mongodb://localhost:27017");
}
return Database.instance;
}
query(sql: string): void {
console.log(`执行: ${sql}`);
}
}
// const db = new Database("..."); // ❌ 构造函数是 private
const db = Database.getInstance(); // ✅ 通过静态方法获取
📝 练习
- 创建一个
Stack<T>类,实现push、pop、peek、isEmpty方法 - 创建抽象类
Logger,有抽象方法write(msg: string),普通方法info/warn/error - 实现
ConsoleLogger和FileLogger继承Logger
// 参考答案
// 1. 泛型栈
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
const numStack = new Stack<number>();
numStack.push(1);
numStack.push(2);
numStack.pop(); // 2
// 2 & 3. Logger
abstract class Logger {
abstract write(msg: string): void;
info(msg: string): void {
this.write(`[INFO] ${msg}`);
}
warn(msg: string): void {
this.write(`[WARN] ${msg}`);
}
error(msg: string): void {
this.write(`[ERROR] ${msg}`);
}
}
class ConsoleLogger extends Logger {
write(msg: string): void {
console.log(msg);
}
}
class FileLogger extends Logger {
constructor(private filepath: string) {
super();
}
write(msg: string): void {
// 模拟写文件
console.log(`写入 ${this.filepath}: ${msg}`);
}
}