Skip to content

第5章 面向对象编程

本章定位:深入学习TypeScript的面向对象编程(OOP),包括类的定义、构造函数、访问修饰符、继承、多态、接口实现等,并与Python的类系统进行对比。


class User {
// 属性
name: string;
age: number;
email: string = "";
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): string {
return `Hello, I'm ${this.name}`;
}
// 静态属性
static MAX_AGE = 120;
// 静态方法
static isValidAge(age: number): boolean {
return age >= 0 && age <= this.MAX_AGE;
}
}
const user = new User("Alice", 30);
console.log(user.greet()); // "Hello, I'm Alice"
console.log(User.isValidAge(30)); // true

Python对比:

class User:
MAX_AGE = 120
def __init__(self, name: str, age: int):
self.name = name
self.age = age
self.email = ""
def greet(self) -> str:
return f"Hello, I'm {self.name}"
@classmethod
def is_valid_age(cls, age: int) -> bool:
return 0 <= age <= cls.MAX_AGE
user = User("Alice", 30)
print(user.greet()) # "Hello, I'm Alice"
print(User.is_valid_age(30)) # True
  1. 类型注解:属性和方法需要类型注解
  2. 访问修饰符:控制属性和方法的可见性
  3. 参数属性:在构造函数参数前加修饰符,自动创建属性
  4. 只读属性:用readonly标记不可修改的属性
class User {
// 参数属性:public name会自动创建name属性
constructor(
public name: string,
public age: number,
private password: string // private不在类外可见
) {}
// 只读属性
readonly id: string;
// 方法
getInfo(): string {
return `${this.name}, ${this.age}`;
}
}
const user = new User("Alice", 30, "secret");
console.log(user.name); // "Alice"
console.log(user.age); // 30
console.log(user.password); // 编译错误!private
class Point {
x: number;
y: number;
}
// 类本身就是类型
const p: Point = new Point();
p.x = 10;
p.y = 20;
// 类实例的类型
function printPoint(p: Point): void {
console.log(`(${p.x}, ${p.y})`);
}
class Animal {
constructor(public name: string) {}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m`);
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name); // 调用父类构造函数
this.breed = breed;
}
// 重写父类方法
move(distance: number = 5): void {
console.log(`${this.name} (${this.breed}) runs ${distance}m`);
}
// 新方法
bark(): void {
console.log("Woof! Woof!");
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.move(); // "Buddy (Golden Retriever) runs 5m"
dog.bark(); // "Woof! Woof!"

Python对比:

class Animal:
def __init__(self, name: str):
self.name = name
def move(self, distance: int = 0):
print(f"{self.name} moved {distance}m")
class Dog(Animal):
def __init__(self, name: str, breed: str):
super().__init__(name)
self.breed = breed
def move(self, distance: int = 5):
print(f"{self.name} ({self.breed}) runs {distance}m")
def bark(self):
print("Woof! Woof!")

class Product {
name: string;
price: number;
category: string;
// 多个构造函数签名
constructor(name: string, price: number, category: string);
constructor(name: string, price: number);
constructor(name: string);
// 实现
constructor(name: string, price?: number, category?: string) {
this.name = name;
this.price = price ?? 0;
this.category = category ?? "uncategorized";
}
}
const p1 = new Product("Apple", 1.5, "fruit");
const p2 = new Product("Book", 20);
const p3 = new Product("Item");
class User {
name: string = "Anonymous";
age: number = 0;
createdAt: Date = new Date();
// 可以直接赋值
isActive: boolean = true;
}
// tsconfig.json: strictPropertyInitialization: true
class User {
name: string; // 编译错误!必须有初始值或在构造函数中初始化
constructor(name: string) {
this.name = name; // OK
}
}
// 或者使用确定赋值断言
class User2 {
name!: string; // 跳过初始化检查
setName(name: string) {
this.name = name;
}
}
class Singleton {
private static instance: Singleton;
private constructor() {}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
const instance = Singleton.getInstance();
// const instance2 = new Singleton(); // 编译错误!构造函数是私有的

5.3 访问修饰符:public、private、protected

Section titled “5.3 访问修饰符:public、private、protected”

所有成员默认是public,可以在任何地方访问:

class User {
public name: string; // 可省略public
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
public greet(): string {
return `Hello, ${this.name}`;
}
}
const user = new User("Alice", 30);
console.log(user.name); // "Alice"(可在类外访问)
console.log(user.greet()); // "Hello, Alice"(可在类外访问)

私有成员只能在类内部访问:

class BankAccount {
public accountNumber: string;
private balance: number;
constructor(accountNumber: string, initialBalance: number) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
}
}
public withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
return true;
}
return false;
}
public getBalance(): number {
return this.balance; // 只能在类内部访问
}
}
const account = new BankAccount("12345", 1000);
console.log(account.accountNumber); // "12345"
console.log(account.balance); // 编译错误!private
console.log(account.getBalance()); // 1000

Python对比:

# Python约定使用下划线前缀表示私有(但不是强制的)
class BankAccount:
def __init__(self, account_number: str, initial_balance: float):
self.account_number = account_number
self._balance = initial_balance # 约定私有
def get_balance(self) -> float:
return self._balance
# Python的"私有"只是约定,可以强制访问
account = BankAccount("12345", 1000)
print(account._balance) # 可以访问,只是不符合PEP8

受保护成员可以在类本身及其子类中访问:

class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected makeSound(): void {
console.log("Some sound");
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
public speak(): void {
console.log(`${this.name} says:`); // 可访问protected的name
this.makeSound(); // 可访问protected的makeSound
}
}
const dog = new Dog("Buddy", "Golden");
console.log(dog.name); // 编译错误!protected
dog.speak(); // OK,输出 "Buddy says: Some sound"
修饰符类内子类类外
public✓✓✓
protected✓✓✗
private✓✗✗

只读属性只能在声明时或构造函数中赋值,之后不能修改:

class User {
readonly id: string;
name: string;
constructor(id: string, name: string) {
this.id = id; // OK,在构造函数中赋值
this.name = name;
}
setName(name: string) {
this.name = name;
this.id = "new-id"; // 编译错误!readonly
}
}
const user = new User("123", "Alice");
user.name = "Bob"; // OK
user.id = "456"; // 编译错误!readonly
// readonly用于类的属性
// const用于变量
class Config {
readonly API_URL = "https://api.example.com";
readonly MAX_RETRIES = 3;
constructor(readonly env: string) {
// 参数属性可以直接标记readonly
}
}
const config = new Config("production");
config.API_URL = "http://other.com"; // 编译错误!

抽象类不能直接实例化,只能作为基类被继承:

abstract class Shape {
abstract name: string;
// 抽象方法:子类必须实现
abstract area(): number;
abstract perimeter(): number;
// 具体方法
describe(): string {
return `This is a ${this.name}`;
}
}
// 不能直接实例化
// const shape = new Shape(); // 编译错误!
class Circle extends Shape {
constructor(public radius: number) {
super();
}
get name(): string {
return "circle";
}
area(): number {
return Math.PI * this.radius ** 2;
}
perimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(public width: number, public height: number) {
super();
}
get name(): string {
return "rectangle";
}
area(): number {
return this.width * this.height;
}
perimeter(): number {
return 2 * (this.width + this.height);
}
}
const circle = new Circle(5);
console.log(circle.area()); // 78.54
console.log(circle.describe()); // "This is a circle"

Python对比:

from abc import ABC, abstractmethod
class Shape(ABC):
@property
@abstractmethod
def name(self) -> str:
pass
@abstractmethod
def area(self) -> float:
pass
@abstractmethod
def perimeter(self) -> float:
pass
def describe(self) -> str:
return f"This is a {self.name}"
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
@property
def name(self) -> str:
return "circle"
def area(self) -> float:
return math.pi * self.radius ** 2

子类可以重写父类的方法:

class Base {
greet(): string {
return "Hello!";
}
}
class Derived extends Base {
// 重写父类方法
greet(): string {
return "Hi there!";
}
// 扩展父类方法
greetFormal(): string {
return `Good day, ${super.greet()}`;
}
}
const derived = new Derived();
console.log(derived.greet()); // "Hi there!"
console.log(derived.greetFormal()); // "Good day, Hi there!"

使用super访问父类的属性和方法:

class Animal {
constructor(public name: string) {}
move(): void {
console.log(`${this.name} is moving`);
}
}
class Bird extends Animal {
constructor(name: string, public wingSpan: number) {
super(name); // 调用父类构造函数
}
move(): void {
console.log(`${this.name} is flying`);
super.move(); // 调用父类方法
}
}

父类引用可以指向子类对象:

class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
speak(): string {
return "Some sound";
}
}
class Dog extends Animal {
speak(): string {
return "Woof!";
}
}
class Cat extends Animal {
speak(): string {
return "Meow!";
}
}
function makeSpeak(animal: Animal): void {
console.log(`${animal.name} says: ${animal.speak()}`);
}
const animals: Animal[] = [
new Dog("Buddy"),
new Cat("Whiskers"),
new Animal("Generic")
];
animals.forEach(makeSpeak);
// "Buddy says: Woof!"
// "Whiskers says: Meow!"
// "Generic says: Some sound"

Python对比:

class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
return "Some sound"
class Dog(Animal):
def speak(self) -> str:
return "Woof!"
class Cat(Animal):
def speak(self) -> str:
return "Meow!"
def make_speak(animal: Animal) -> None:
print(f"{animal.name} says: {animal.speak()}")
animals = [Dog("Buddy"), Cat("Whiskers"), Animal("Generic")]
for animal in animals:
make_speak(animal)

使用implements关键字让类实现接口:

interface Printable {
print(): void;
}
interface Serializable {
serialize(): string;
}
class Report implements Printable, Serializable {
constructor(public title: string, public content: string) {}
print(): void {
console.log(`Report: ${this.title}`);
console.log(this.content);
}
serialize(): string {
return JSON.stringify({ title: this.title, content: this.content });
}
}
const report = new Report("Q1 Results", "Sales increased by 20%");
report.print();
const json = report.serialize();
interface User {
name: string;
age: number;
email?: string;
}
class UserAccount implements User {
name: string;
age: number;
email: string = "";
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// User接口不需要实现email,但类可以有自己的额外属性
greet(): string {
return `Hello, I'm ${this.name}`;
}
}
特性抽象类接口
属性可以有属性只能有方法签名
方法实现可以有实现默认无实现(TS 4.8+可默认实现)
多继承不能可以实现多个
构造函数可以有不能有
访问修饰符可以有方法默认public
// 抽象类:适合有共享实现的情况
abstract class Base {
abstract doSomething(): void;
log(): void {
console.log("Logging...");
}
}
// 接口:适合定义契约
interface Reader {
read(): string;
}
interface Writer {
write(content: string): void;
}

属于类本身,而不是实例:

class MathUtils {
// 静态属性
static PI = 3.14159;
// 静态方法
static circleArea(radius: number): number {
return this.PI * radius * radius;
}
static circleCircumference(radius: number): number {
return 2 * this.PI * radius;
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.circleArea(5)); // 78.54
console.log(MathUtils.circleCircumference(5)); // 31.42
// 无需创建实例
const area = MathUtils.circleArea(10);

静态属性初始化:

class Configuration {
static instance: Configuration;
static readonly DEFAULT_TIMEOUT = 30;
static readonly DEFAULT_PORT = 8080;
timeout: number;
port: number;
private constructor() {
this.timeout = Configuration.DEFAULT_TIMEOUT;
this.port = Configuration.DEFAULT_PORT;
}
static getInstance(): Configuration {
if (!Configuration.instance) {
Configuration.instance = new Configuration();
}
return Configuration.instance;
}
}

Python对比:

class Configuration:
DEFAULT_TIMEOUT = 30
DEFAULT_PORT = 8080
def __init__(self):
self.timeout = self.DEFAULT_TIMEOUT
self.port = self.DEFAULT_PORT
@classmethod
def get_instance(cls):
if not hasattr(cls, '_instance'):
cls._instance = cls()
return cls._instance
class Counter {
private static count = 0;
static increment(): number {
return ++Counter.count;
}
static getCount(): number {
return Counter.count;
}
}
console.log(Counter.getCount()); // 0
Counter.increment();
Counter.increment();
console.log(Counter.getCount()); // 2

PythonTypeScript说明
class Foo:class Foo {}类定义
selfthis引用自身
def __init__(self, x):constructor(x: type) {}构造函数
def method(self):method(): void {}实例方法
@staticmethodstatic method(): void {}静态方法
@classmethodstatic method(): void {} (调用方式不同)类方法
_privateprivate私有(Python是约定)
__slot__无对应属性限制
@propertyget property(): type {}属性装饰器
# Python
class User:
def __init__(self, name: str, age: int, email: str = ""):
self.name = name
self.age = age
self.email = email
// TypeScript
class User {
constructor(
public name: string,
public age: number,
public email: string = ""
) {}
}
# Python
class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
return "..."
class Dog(Animal):
def __init__(self, name: str, breed: str):
super().__init__(name)
self.breed = breed
def speak(self) -> str:
return "Woof!"
// TypeScript
class Animal {
constructor(public name: string) {}
speak(): string {
return "...";
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name);
}
speak(): string {
return "Woof!";
}
}
# Python:约定大于强制
class BankAccount:
def __init__(self, balance: float):
self._balance = balance # 私有约定
self.__private = 0 # 名称重整(name mangling)
def get_balance(self):
return self._balance
# 从外部仍可访问(只是不符合约定)
account = BankAccount(100)
print(account._balance) # 100,可以访问但不符合PEP8
print(account._BankAccount__private) # 0,名称重整
// TypeScript:强制访问控制
class BankAccount {
private balance: number;
constructor(balance: number) {
this.balance = balance;
}
getBalance(): number {
return this.balance;
}
}
const account = new BankAccount(100);
console.log(account.balance); // 编译错误!private
# Python @property
class User:
@property
def full_name(self) -> str:
return f"{self.first_name} {self.last_name}"
@full_name.setter
def full_name(self, value: str):
first, last = value.split()
self.first_name = first
self.last_name = last
// TypeScript getter/setter
class User {
private _firstName: string = "";
private _lastName: string = "";
get fullName(): string {
return `${this._firstName} ${this._lastName}`;
}
set fullName(value: string) {
const [first, last] = value.split(" ");
this._firstName = first;
this._lastName = last;
}
}
# Python dataclass
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
email: str = ""
def greet(self) -> str:
return f"Hello, {self.name}"
// TypeScript:手写或用库
// 手写方式
class User {
constructor(
public name: string,
public age: number,
public email: string = ""
) {}
greet(): string {
return `Hello, ${this.name}`;
}
}

装饰器增强现有类的功能:

class User {
name: string;
constructor(name: string) {
this.name = name;
}
greet(): string {
return `Hello, ${this.name}`;
}
}
// 装饰器函数
function withLogging<T extends { greet(): string }>(target: T): T & { log(): void } {
const originalGreet = target.greet.bind(target);
target.greet = function(): string {
console.log(`[LOG] Calling greet for ${this.name}`);
return originalGreet();
} as typeof originalGreet;
return target as T & { log(): void };
}
const user = withLogging(new User("Alice"));
console.log(user.greet()); // [LOG] Calling greet for Alice + Hello, Alice

创建带参数的装饰器:

function sealed(target: Function): void {
Object.seal(target);
Object.seal(target.prototype);
}
function timeout(ms: number) {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
setTimeout(() => {
original.apply(this, args);
}, ms);
};
return descriptor;
};
}
class DataService {
@timeout(1000)
fetchData(): void {
console.log("Fetching data...");
}
}
function readonly(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
): PropertyDescriptor {
descriptor.writable = false;
return descriptor;
}
class Point {
constructor(public x: number, public y: number) {}
@readonly
distance(): number {
return Math.sqrt(this.x ** 2 + this.y ** 2);
}
}
const p = new Point(3, 4);
console.log(p.distance()); // 5
// p.distance = () => 0; // 编译错误!readonly

Python对比:

# Python装饰器
import functools
def log_calls(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
class Calculator:
@log_calls
def add(self, a: int, b: int) -> int:
return a + b

本章学习了TypeScript的面向对象编程:

  1. 类定义:

    • 使用class关键字
    • 构造函数是constructor
    • 参数属性用修饰符简化
  2. 访问修饰符:

    • public:到处可访问
    • protected:类及子类可访问
    • private:仅类内可访问
    • readonly:只读
  3. 继承与多态:

    • 使用extends继承
    • super()调用父类构造函数
    • 方法可以重写
  4. 接口实现:

    • implements关键字
    • 一个类可实现多个接口
  5. 静态成员:

    • static关键字
    • 属于类本身,不是实例
  6. Python对比:

    • Python用self,TypeScript用this
    • Python访问控制是约定,TypeScript是强制的
    • TypeScript有明确的public/private/protected

定义一个BankAccount类,包含:

  • 属性:accountNumber(只读)、balance
  • 方法:deposit(amount)、withdraw(amount)
  • 验证余额不能为负

创建Shape抽象类及其子类Circle和Rectangle,实现多态计算面积。

创建Sortable接口,实现sort()方法。让Person类实现该接口。

创建IdGenerator类,使用静态方法生成唯一ID。

创建一个@debounce装饰器,延迟方法执行。



下一章我们将学习TypeScript的高级类型,包括泛型约束、条件类型、映射类型、工具类型等,掌握类型级别的编程技巧。