类是面向对象编程的核心概念。TypeScript 扩展了 JavaScript 的类,添加了类型注解、访问修饰符、抽象类等特性。本章将深入讲解 TypeScript 类的完整功能。
class Person {
// 属性声明
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): string {
return `Hello, my name is ${this.name}`;
}
// 获取年龄
getAge(): number {
return this.age;
}
}
// 创建实例
const alice = new Person("Alice", 25);
console.log(alice.greet()); // "Hello, my name is Alice"
// 使用 public 修饰符简化属性声明
class Person2 {
constructor(
public name: string,
public age: number,
private id: string
) {}
greet(): string {
return `Hello, my name is ${this.name}`;
}
}
const bob = new Person2("Bob", 30, "user-123");
console.log(bob.name); // "Bob"
// console.log(bob.id); // Error: 私有属性
class Animal {
public name: string; // 默认就是 public
constructor(name: string) {
this.name = name;
}
public move(distance: number): void {
console.log(`${this.name} moved ${distance}m`);
}
}
const dog = new Animal("Dog");
console.log(dog.name); // OK
dog.move(10); // OK
class BankAccount {
private balance: number = 0;
private readonly accountId: string;
constructor(accountId: string, initialBalance: number) {
this.accountId = accountId;
this.balance = initialBalance;
}
deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
}
withdraw(amount: number): boolean {
if (amount > 0 && this.balance >= amount) {
this.balance -= amount;
return true;
}
return false;
}
getBalance(): number {
return this.balance;
}
}
const account = new BankAccount("ACC-001", 1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// console.log(account.balance); // Error: 私有属性
// account.balance = 1000000; // Error
class Vehicle {
protected brand: string;
protected speed: number = 0;
constructor(brand: string) {
this.brand = brand;
}
protected accelerate(amount: number): void {
this.speed += amount;
}
getSpeed(): number {
return this.speed;
}
}
class Car extends Vehicle {
private model: string;
constructor(brand: string, model: string) {
super(brand);
this.model = model;
}
speedUp(): void {
// 可以访问父类的 protected 成员
this.accelerate(10);
console.log(`${this.brand} ${this.model} speeding up to ${this.speed}km/h`);
}
}
const car = new Car("Toyota", "Camry");
car.speedUp(); // OK
// car.accelerate(20); // Error: 受保护的方法
// car.brand; // Error: 受保护的属性
class Employee {
readonly id: string;
name: string;
readonly hireDate: Date;
constructor(id: string, name: string) {
this.id = id;
this.name = name;
this.hireDate = new Date();
}
// 不能在方法中修改 readonly 属性
// updateId(newId: string): void {
// this.id = newId; // Error
// }
}
const emp = new Employee("E001", "Alice");
console.log(emp.id);
// emp.id = "E002"; // Error
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m`);
}
makeSound(): void {
console.log("Some sound");
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name); // 必须调用父类构造函数
this.breed = breed;
}
// 重写方法
makeSound(): void {
console.log("Woof! Woof!");
}
fetch(): void {
console.log(`${this.name} is fetching the ball`);
}
getBreed(): string {
return this.breed;
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.move(10); // 继承的方法
dog.makeSound(); // 重写的方法
dog.fetch(); // 子类的方法
class Rectangle {
constructor(
protected width: number,
protected height: number
) {}
getArea(): number {
return this.width * this.height;
}
describe(): string {
return `Rectangle: ${this.width}x${this.height}`;
}
}
class Square extends Rectangle {
constructor(side: number) {
super(side, side); // 调用父类构造函数
}
describe(): string {
// 调用父类方法
const baseDescription = super.describe();
return `${baseDescription} (Square)`;
}
}
const square = new Square(5);
console.log(square.getArea()); // 25
console.log(square.describe()); // "Rectangle: 5x5 (Square)"
class Person {
constructor(protected name: string) {}
introduce(): string {
return `I am ${this.name}`;
}
}
class Student extends Person {
constructor(
name: string,
private grade: number
) {
super(name);
}
// 方法重写
introduce(): string {
const base = super.introduce();
return `${base}, a student in grade ${this.grade}`;
}
}
// 方法重载
class Calculator {
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: any, b: any): any {
if (typeof a === "number" && typeof b === "number") {
return a + b;
}
return String(a) + String(b);
}
}
const calc = new Calculator();
console.log(calc.add(1, 2)); // 3
console.log(calc.add("a", "b")); // "ab"
// 抽象类不能实例化
abstract class Shape {
protected color: string;
constructor(color: string) {
this.color = color;
}
// 抽象方法必须在子类中实现
abstract getArea(): number;
abstract getPerimeter(): number;
// 普通方法
describe(): string {
return `A ${this.color} shape`;
}
}
// 具体类必须实现所有抽象方法
class Circle extends Shape {
constructor(
color: string,
private radius: number
) {
super(color);
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(
color: string,
private width: number,
private height: number
) {
super(color);
}
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
}
// 使用
const shapes: Shape[] = [
new Circle("red", 5),
new Rectangle("blue", 4, 6)
];
shapes.forEach(shape => {
console.log(`${shape.describe()}: Area=${shape.getArea().toFixed(2)}`);
});
// 抽象类不能实例化
// const shape = new Shape("green"); // Error
class Employee {
private _name: string;
private _salary: number;
constructor(name: string, salary: number) {
this._name = name;
this._salary = salary;
}
// getter
get name(): string {
return this._name;
}
// setter
set name(value: string) {
if (value.length < 2) {
throw new Error("Name must be at least 2 characters");
}
this._name = value;
}
get salary(): number {
return this._salary;
}
// 只读属性(只有 getter)
get annualSalary(): number {
return this._salary * 12;
}
}
const emp = new Employee("Alice", 5000);
console.log(emp.name); // "Alice"
emp.name = "Bob"; // 使用 setter
console.log(emp.annualSalary); // 60000
// emp.salary = 6000; // Error: 没有 setter
class Temperature {
private _celsius: number;
constructor(celsius: number) {
this._celsius = celsius;
}
get celsius(): number {
return this._celsius;
}
set celsius(value: number) {
this._celsius = value;
}
get fahrenheit(): number {
return (this._celsius * 9 / 5) + 32;
}
set fahrenheit(value: number) {
this._celsius = (value - 32) * 5 / 9;
}
get kelvin(): number {
return this._celsius + 273.15;
}
}
const temp = new Temperature(25);
console.log(temp.fahrenheit); // 77
console.log(temp.kelvin); // 298.15
temp.fahrenheit = 100;
console.log(temp.celsius); // 37.777...
class MathUtils {
// 静态属性
static readonly PI: number = 3.14159;
static readonly E: number = 2.71828;
// 静态方法
static circleArea(radius: number): number {
return this.PI * radius * radius;
}
static max(a: number, b: number): number {
return a > b ? a : b;
}
// 静态工厂方法
static createRandom(): number {
return Math.random();
}
}
// 无需实例化即可使用
console.log(MathUtils.PI);
console.log(MathUtils.circleArea(5));
console.log(MathUtils.max(10, 20));
// 不能通过实例访问
// const utils = new MathUtils();
// utils.PI; // Error
class Database {
private static instance: Database;
private connection: string;
private constructor(connectionString: string) {
this.connection = connectionString;
}
static getInstance(connectionString: string): Database {
if (!Database.instance) {
Database.instance = new Database(connectionString);
}
return Database.instance;
}
query(sql: string): any {
console.log(`Executing: ${sql}`);
return [];
}
}
// 使用
const db1 = Database.getInstance("mysql://localhost");
const db2 = Database.getInstance("mysql://remote"); // 返回同一个实例
console.log(db1 === db2); // true
interface Printable {
print(): void;
}
interface Loggable {
log(): void;
}
// 类可以实现多个接口
class Document implements Printable, Loggable {
constructor(private content: string) {}
print(): void {
console.log("Printing:", this.content);
}
log(): void {
console.log("Logging document content");
}
}
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
// 接口继承类
interface Point3D extends Point {
z: number;
}
const point3d: Point3D = {
x: 1,
y: 2,
z: 3
};
class GenericStack<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;
}
size(): number {
return this.items.length;
}
}
// 使用
const numberStack = new GenericStack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2
const stringStack = new GenericStack<string>();
stringStack.push("hello");
stringStack.push("world");
console.log(stringStack.pop()); // "world"
abstract class Component {
abstract id: string; // 抽象属性
abstract render(): void;
mount(): void {
console.log(`Mounting component ${this.id}`);
this.render();
}
}
class ButtonComponent extends Component {
id: string = "button-" + Math.random().toString(36);
render(): void {
console.log("Rendering button");
}
}
本章我们学习了:
1. 类基础 - 属性声明、构造函数、方法
2. 访问修饰符 - public、private、protected、readonly
3. 继承 - extends、super、方法重写
4. 抽象类 - abstract、抽象方法
5. 存取器 - getter 和 setter
6. 静态成员 - 静态属性、静态方法、单例模式
7. 类与接口 - 类实现接口、接口继承类
8. 泛型类 - 类型参数化
设计一个 BankAccount 类,要求:
参考答案
class BankAccount {
private _balance: number = 0;
private readonly _accountNumber: string;
private readonly _ownerName: string;
private _transactionHistory: string[] = [];
constructor(accountNumber: string, ownerName: string, initialBalance: number = 0) {
this._accountNumber = accountNumber;
this._ownerName = ownerName;
this._balance = initialBalance;
this._logTransaction("ACCOUNT_OPENED", initialBalance);
}
get accountNumber(): string {
return this._accountNumber;
}
get ownerName(): string {
return this._ownerName;
}
get balance(): number {
return this._balance;
}
get transactionHistory(): readonly string[] {
return [...this._transactionHistory];
}
deposit(amount: number): void {
if (amount <= 0) {
throw new Error("Deposit amount must be positive");
}
this._balance += amount;
this._logTransaction("DEPOSIT", amount);
}
withdraw(amount: number): void {
if (amount <= 0) {
throw new Error("Withdrawal amount must be positive");
}
if (amount > this._balance) {
throw new Error("Insufficient funds");
}
this._balance -= amount;
this._logTransaction("WITHDRAWAL", amount);
}
transfer(amount: number, targetAccount: BankAccount): void {
this.withdraw(amount);
targetAccount.deposit(amount);
this._logTransaction(`TRANSFER_TO_${targetAccount.accountNumber}`, amount);
}
private _logTransaction(type: string, amount: number): void {
const timestamp = new Date().toISOString();
this._transactionHistory.push(
`[${timestamp}] ${type}: $${amount.toFixed(2)}, Balance: $${this._balance.toFixed(2)}`
);
}
}
// 使用
const account1 = new BankAccount("ACC-001", "Alice", 1000);
const account2 = new BankAccount("ACC-002", "Bob", 500);
account1.deposit(500);
account1.transfer(200, account2);
console.log(account1.balance); // 1300
console.log(account2.balance); // 700
console.log(account1.transactionHistory);
设计一个图形类系统,要求:
参考答案
abstract class Shape {
constructor(protected color: string) {}
abstract getArea(): number;
abstract getPerimeter(): number;
abstract describe(): string;
getColor(): string {
return this.color;
}
setColor(color: string): void {
this.color = color;
}
}
class Circle extends Shape {
constructor(color: string, private radius: number) {
super(color);
if (radius <= 0) {
throw new Error("Radius must be positive");
}
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
describe(): string {
return `Circle(${this.radius}) - ${this.color}`;
}
getRadius(): number {
return this.radius;
}
setRadius(radius: number): void {
if (radius <= 0) {
throw new Error("Radius must be positive");
}
this.radius = radius;
}
}
class Rectangle extends Shape {
constructor(
color: string,
private width: number,
private height: number
) {
super(color);
if (width <= 0 || height <= 0) {
throw new Error("Dimensions must be positive");
}
}
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
describe(): string {
return `Rectangle(${this.width}x${this.height}) - ${this.color}`;
}
isSquare(): boolean {
return this.width === this.height;
}
}
class Triangle extends Shape {
constructor(
color: string,
private a: number,
private b: number,
private c: number
) {
super(color);
// 验证三角形不等式
if (a + b <= c || a + c <= b || b + c <= a) {
throw new Error("Invalid triangle sides");
}
}
getArea(): number {
// 海伦公式
const s = this.getPerimeter() / 2;
return Math.sqrt(s * (s - this.a) * (s - this.b) * (s - this.c));
}
getPerimeter(): number {
return this.a + this.b + this.c;
}
describe(): string {
return `Triangle(${this.a}, ${this.b}, ${this.c}) - ${this.color}`;
}
}
// 形状工厂
class ShapeFactory {
static createCircle(color: string, radius: number): Circle {
return new Circle(color, radius);
}
static createRectangle(color: string, width: number, height: number): Rectangle {
return new Rectangle(color, width, height);
}
static createTriangle(color: string, a: number, b: number, c: number): Triangle {
return new Triangle(color, a, b, c);
}
static createRandomShape(): Shape {
const shapes = ["circle", "rectangle", "triangle"];
const randomShape = shapes[Math.floor(Math.random() * shapes.length)];
const color = ["red", "blue", "green"][Math.floor(Math.random() * 3)];
switch (randomShape) {
case "circle":
return new Circle(color, Math.random() * 10 + 1);
case "rectangle":
return new Rectangle(color, Math.random() * 10 + 1, Math.random() * 10 + 1);
case "triangle":
return new Triangle(color, 3, 4, 5);
default:
throw new Error("Unknown shape");
}
}
}
// 使用
const shapes: Shape[] = [
ShapeFactory.createCircle("red", 5),
ShapeFactory.createRectangle("blue", 4, 6),
ShapeFactory.createTriangle("green", 3, 4, 5)
];
shapes.forEach(shape => {
console.log(`${shape.describe()}`);
console.log(` Area: ${shape.getArea().toFixed(2)}`);
console.log(` Perimeter: ${shape.getPerimeter().toFixed(2)}`);
});
实现一个泛型的 Collection 类,要求:
参考答案
class Collection<T> implements Iterable<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
remove(item: T): boolean {
const index = this.items.indexOf(item);
if (index > -1) {
this.items.splice(index, 1);
return true;
}
return false;
}
removeAt(index: number): T | undefined {
if (index < 0 || index >= this.items.length) {
return undefined;
}
return this.items.splice(index, 1)[0];
}
get(index: number): T | undefined {
return this.items[index];
}
contains(item: T): boolean {
return this.items.includes(item);
}
size(): number {
return this.items.length;
}
isEmpty(): boolean {
return this.items.length === 0;
}
clear(): void {
this.items = [];
}
toArray(): T[] {
return [...this.items];
}
// 高阶函数
filter(predicate: (item: T) => boolean): Collection<T> {
const result = new Collection<T>();
for (const item of this.items) {
if (predicate(item)) {
result.add(item);
}
}
return result;
}
map<U>(transform: (item: T) => U): Collection<U> {
const result = new Collection<U>();
for (const item of this.items) {
result.add(transform(item));
}
return result;
}
reduce<U>(reducer: (acc: U, item: T) => U, initial: U): U {
let accumulator = initial;
for (const item of this.items) {
accumulator = reducer(accumulator, item);
}
return accumulator;
}
find(predicate: (item: T) => boolean): T | undefined {
return this.items.find(predicate);
}
every(predicate: (item: T) => boolean): boolean {
return this.items.every(predicate);
}
some(predicate: (item: T) => boolean): boolean {
return this.items.some(predicate);
}
// 实现迭代器
[Symbol.iterator](): Iterator<T> {
let index = 0;
const items = this.items;
return {
next(): IteratorResult<T> {
if (index < items.length) {
return { value: items[index++], done: false };
}
return { value: undefined as any, done: true };
}
};
}
// 排序
sort(compareFn?: (a: T, b: T) => number): Collection<T> {
const result = new Collection<T>();
result.items = [...this.items].sort(compareFn);
return result;
}
// 反转
reverse(): Collection<T> {
const result = new Collection<T>();
result.items = [...this.items].reverse();
return result;
}
}
// 使用
const numbers = new Collection<number>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
console.log([...numbers]); // [5, 2, 8, 1]
const evens = numbers.filter(n => n % 2 === 0);
console.log([...evens]); // [2, 8]
const doubled = numbers.map(n => n * 2);
console.log([...doubled]); // [10, 4, 16, 2]
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 16
const sorted = numbers.sort((a, b) => a - b);
console.log([...sorted]); // [1, 2, 5, 8]