第二章:面向对象编程
1. 类与对象
1.1 类的基本定义
typescript
// 基础类定义
class Person {
// 属性声明
name: string;
age: number;
private id: number;
protected email: string;
public readonly createdAt: Date;
// 静态属性
static species: string = "Homo sapiens";
private static instanceCount: number = 0;
// 构造函数
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
this.id = Math.random();
this.createdAt = new Date();
Person.instanceCount++;
}
// 实例方法
greet(): string {
return `Hello, my name is ${this.name}`;
}
// 私有方法
private validateEmail(): boolean {
return this.email.includes("@");
}
// 受保护的方法
protected getFormattedEmail(): string {
return `Email: ${this.email}`;
}
// 静态方法
static getInstanceCount(): number {
return Person.instanceCount;
}
// Getter 和 Setter
get fullInfo(): string {
return `${this.name}, ${this.age} years old`;
}
set updateName(newName: string) {
if (newName.length >= 2) {
this.name = newName;
} else {
throw new Error("Name must be at least 2 characters long");
}
}
// 抽象方法模式(通过接口实现)
abstractMethod?(): void; // 可选抽象方法
}
// 使用类
const person = new Person("Alice", 30, "alice@example.com");
console.log(person.greet());
console.log(person.fullInfo);
console.log(Person.species);
console.log(Person.getInstanceCount());1.2 构造函数参数属性
typescript
// 简化的类定义(参数属性)
class Employee {
// 直接在构造函数中定义属性
constructor(
public name: string,
private department: string,
protected salary: number,
readonly employeeId: string,
) {}
getDetails(): string {
return `${this.name} works in ${this.department}`;
}
}
// 等价于传统的写法
class TraditionalEmployee {
public name: string;
private department: string;
protected salary: number;
readonly employeeId: string;
constructor(
name: string,
department: string,
salary: number,
employeeId: string,
) {
this.name = name;
this.department = department;
this.salary = salary;
this.employeeId = employeeId;
}
}1.3 访问器装饰器
typescript
function LogAccess(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalGet = descriptor.get;
const originalSet = descriptor.set;
if (originalGet) {
descriptor.get = function () {
console.log(`Getting ${propertyKey}`);
return originalGet.call(this);
};
}
if (originalSet) {
descriptor.set = function (value: any) {
console.log(`Setting ${propertyKey} to ${value}`);
originalSet.call(this, value);
};
}
return descriptor;
}
class Product {
private _price: number = 0;
@LogAccess
get price(): number {
return this._price;
}
@LogAccess
set price(value: number) {
if (value >= 0) {
this._price = value;
}
}
}2. 继承与多态
2.1 基础继承
typescript
// 基类
class Animal {
constructor(
public name: string,
public age: number,
protected species: string,
) {}
// 公共方法
makeSound(): string {
return "Some generic animal sound";
}
// 受保护的方法(子类可用)
protected getInfo(): string {
return `${this.name} is a ${this.species}`;
}
// 静态方法继承
static describe(): string {
return "This is an Animal class";
}
}
// 派生类
class Dog extends Animal {
private breed: string;
constructor(name: string, age: number, breed: string) {
super(name, age, "Canine"); // 必须首先调用 super()
this.breed = breed;
}
// 方法重写
makeSound(): string {
return "Woof! Woof!";
}
// 新增方法
fetch(item: string): string {
return `${this.name} fetched the ${item}`;
}
// 访问基类受保护的方法
getDogInfo(): string {
return `${this.getInfo()} of breed ${this.breed}`;
}
// 重写静态方法
static describe(): string {
return "This is a Dog class";
}
}
// 多层继承
class GermanShepherd extends Dog {
constructor(name: string, age: number) {
super(name, age, "German Shepherd");
}
// 进一步重写方法
makeSound(): string {
return "Deep Woof!";
}
// 新增特有方法
guard(): string {
return `${this.name} is guarding the house`;
}
}
// 使用示例
const dog = new Dog("Buddy", 3, "Golden Retriever");
console.log(dog.makeSound()); // "Woof! Woof!"
console.log(dog.fetch("ball")); // "Buddy fetched the ball"
console.log(dog.getDogInfo()); // "Buddy is a Canine of breed Golden Retriever"
const shepherd = new GermanShepherd("Rex", 4);
console.log(shepherd.makeSound()); // "Deep Woof!"
console.log(shepherd.guard()); // "Rex is guarding the house"2.2 抽象类
抽象类是专门用来被其他类所继承的类,它只能被其他类所继承不能用来创建实例。抽象方法没有方法体只能定义在抽象类中,继承抽象类时抽象方法必须要实现。
typescript
// 抽象基类
abstract class Shape {
constructor(public color: string) {}
// 抽象方法(必须由子类实现)
abstract getArea(): number;
abstract getPerimeter(): number;
// 具体方法
describe(): string {
return `A ${this.color} shape`;
}
// 静态方法
static compareArea(shape1: Shape, shape2: Shape): number {
return shape1.getArea() - shape2.getArea();
}
}
// 具体子类
class Circle extends Shape {
constructor(
color: string,
public radius: number,
) {
super(color);
}
// 实现抽象方法
getArea(): number {
return Math.PI * this.radius * this.radius;
}
getPerimeter(): number {
return 2 * Math.PI * this.radius;
}
// 重写具体方法
describe(): string {
return `${super.describe()} with radius ${this.radius}`;
}
}
class Rectangle extends Shape {
constructor(
color: string,
public width: number,
public height: number,
) {
super(color);
}
// 实现抽象方法
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
// 新增方法
isSquare(): boolean {
return this.width === this.height;
}
}
// 使用抽象类
const circle = new Circle("red", 5);
const rectangle = new Rectangle("blue", 4, 6);
console.log(circle.getArea()); // 78.53981633974483
console.log(rectangle.getPerimeter()); // 20
console.log(Shape.compareArea(circle, rectangle)); // 58.539816339744832.3 多态与类型守卫
typescript
// 多态示例
class Vehicle {
constructor(public brand: string) {}
startEngine(): string {
return "Starting vehicle engine...";
}
}
class Car extends Vehicle {
constructor(
brand: string,
public doors: number,
) {
super(brand);
}
startEngine(): string {
return `Starting ${this.brand} car with ${this.doors} doors...`;
}
honk(): string {
return "Beep beep!";
}
}
class Motorcycle extends Vehicle {
constructor(
brand: string,
public hasSidecar: boolean,
) {
super(brand);
}
startEngine(): string {
return `Starting ${this.brand} motorcycle...`;
}
wheelie(): string {
return "Doing a wheelie!";
}
}
// 多态使用
const vehicles: Vehicle[] = [
new Car("Toyota", 4),
new Motorcycle("Harley", false),
new Car("Honda", 2),
];
vehicles.forEach((vehicle) => {
console.log(vehicle.startEngine());
// 类型守卫检查
if (vehicle instanceof Car) {
console.log(vehicle.honk());
}
if (vehicle instanceof Motorcycle) {
console.log(vehicle.wheelie());
}
});
// 自定义类型守卫
function isCar(vehicle: Vehicle): vehicle is Car {
return (vehicle as Car).honk !== undefined;
}
function isMotorcycle(vehicle: Vehicle): vehicle is Motorcycle {
return (vehicle as Motorcycle).wheelie !== undefined;
}
// 使用自定义类型守卫
vehicles.forEach((vehicle) => {
if (isCar(vehicle)) {
console.log(`This car has ${vehicle.doors} doors`);
} else if (isMotorcycle(vehicle)) {
console.log(
`This motorcycle ${vehicle.hasSidecar ? "has" : "does not have"} a sidecar`,
);
}
});3. 接口
接口的作用类似于抽象类,不同点在于接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法。接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性。
3.1 基础接口
typescript
// 对象类型接口
interface User {
readonly id: number; // 只读属性
name: string; // 必需属性
email: string; // 必需属性
age?: number; // 可选属性
[key: string]: any; // 索引签名
}
// 函数类型接口
interface SearchFunction {
(source: string, subString: string): boolean;
}
// 可索引类型接口
interface StringArray {
[index: number]: string;
length: number;
}
// 类类型接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
// 构造函数接口
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
// 混合类型接口
interface Counter {
(start: number): string; // 函数调用签名
interval: number; // 属性
reset(): void; // 方法
}
// 接口继承
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
// 实现接口
class DigitalClock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date): void {
this.currentTime = d;
}
}
// 使用接口
function printUser(user: User): void {
console.log(`User: ${user.name}, Email: ${user.email}`);
if (user.age) {
console.log(`Age: ${user.age}`);
}
}
const mySearch: SearchFunction = function (src, sub) {
return src.search(sub) > -1;
};
const square: Square = {
color: "blue",
penWidth: 5.0,
sideLength: 10,
};3.2 高级接口特性
typescript
// 可选属性和只读属性
interface Config {
readonly apiUrl: string;
timeout?: number;
retryCount?: number;
headers?: Record<string, string>;
}
// 函数接口
interface Transformer<T, U> {
(input: T): U;
}
// 构造器接口
interface AnimalConstructor {
new (name: string): Animal;
}
// 混合接口
interface Dictionary {
[key: string]: any;
size: number;
clear(): void;
}
// 接口继承多个接口
interface Drawable {
draw(): void;
}
interface Resizable {
resize(scale: number): void;
}
interface UIElement extends Drawable, Resizable {
position: { x: number; y: number };
}
// 实现多个接口
class Button implements Drawable, Resizable {
position = { x: 0, y: 0 };
draw(): void {
console.log("Drawing button");
}
resize(scale: number): void {
console.log(`Resizing to scale ${scale}`);
}
}
// 接口作为约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// 接口与类型别名区别
type StringOrNumber = string | number;
type Text = string | { text: string };
// 接口可以重复声明(自动合并)
interface Box {
height: number;
width: number;
}
interface Box {
scale: number;
// height: string; // 错误:后续属性声明必须类型相同
}
const box: Box = {
height: 5,
width: 6,
scale: 10,
};3.3 接口的实际应用
typescript
// API 响应接口
interface ApiResponse<T = any> {
data: T;
status: number;
message: string;
timestamp: string;
pagination?: {
page: number;
pageSize: number;
total: number;
totalPages: number;
};
}
// 用户相关接口
interface UserProfile {
id: string;
username: string;
email: string;
avatar?: string;
createdAt: Date;
updatedAt: Date;
}
interface LoginCredentials {
email: string;
password: string;
rememberMe?: boolean;
}
// 服务层接口
interface UserService {
login(credentials: LoginCredentials): Promise<ApiResponse<UserProfile>>;
logout(): Promise<ApiResponse<void>>;
getProfile(userId: string): Promise<ApiResponse<UserProfile>>;
updateProfile(
userId: string,
data: Partial<UserProfile>,
): Promise<ApiResponse<UserProfile>>;
}
// 组件 Props 接口
interface ButtonProps {
text: string;
onClick: () => void;
type?: "primary" | "secondary" | "danger";
disabled?: boolean;
loading?: boolean;
icon?: React.ReactNode;
className?: string;
}
// 状态管理接口
interface AppState {
user: UserProfile | null;
isLoading: boolean;
error: string | null;
theme: "light" | "dark";
language: string;
}
interface AppActions {
setUser: (user: UserProfile | null) => void;
setLoading: (loading: boolean) => void;
setError: (error: string | null) => void;
toggleTheme: () => void;
setLanguage: (lang: string) => void;
}
// 表单验证接口
interface ValidationRule {
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: RegExp;
validate?: (value: any) => boolean | string;
}
interface FormField<T = any> {
value: T;
error?: string;
touched: boolean;
rules?: ValidationRule[];
}
// 实现示例
class UserServiceImpl implements UserService {
async login(
credentials: LoginCredentials,
): Promise<ApiResponse<UserProfile>> {
// API 调用逻辑
return {
data: {
id: "123",
username: credentials.email.split("@")[0],
email: credentials.email,
createdAt: new Date(),
updatedAt: new Date(),
},
status: 200,
message: "Login successful",
timestamp: new Date().toISOString(),
};
}
async logout(): Promise<ApiResponse<void>> {
return {
data: undefined,
status: 200,
message: "Logout successful",
timestamp: new Date().toISOString(),
};
}
async getProfile(userId: string): Promise<ApiResponse<UserProfile>> {
// 实现获取用户资料逻辑
return {
data: {
id: userId,
username: "testuser",
email: "test@example.com",
createdAt: new Date(),
updatedAt: new Date(),
},
status: 200,
message: "Profile retrieved",
timestamp: new Date().toISOString(),
};
}
async updateProfile(
userId: string,
data: Partial<UserProfile>,
): Promise<ApiResponse<UserProfile>> {
// 实现更新用户资料逻辑
return {
data: {
id: userId,
username: data.username || "testuser",
email: data.email || "test@example.com",
createdAt: new Date(),
updatedAt: new Date(),
},
status: 200,
message: "Profile updated",
timestamp: new Date().toISOString(),
};
}
}4. 泛型
4.1 基础泛型
typescript
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
constructor(zeroValue: T, add: (x: T, y: T) => T) {
this.zeroValue = zeroValue;
this.add = add;
}
}
// 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// 多个类型参数
function merge<U, V>(obj1: U, obj2: V): U & V {
return { ...obj1, ...obj2 };
}
// 泛型参数默认值
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
// 使用示例
const myIdentity: GenericIdentityFn<number> = identity;
const stringArray = createArray(3, "hello");
const numberArray = createArray<number>(3, 42);
// 泛型工具类
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;
}
size(): number {
return this.items.length;
}
clear(): void {
this.items = [];
}
}
// 使用泛型栈
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
console.log(stringStack.pop()); // "world"4.2 高级泛型技巧
typescript
// 条件类型
type IsString<T> = T extends string ? true : false;
type Result1 = IsString<string>; // true
type Result2 = IsString<number>; // false
// 条件类型与 infer 关键字
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type ElementType<T> = T extends (infer U)[] ? U : never;
// 映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
// 实用工具类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Record<K extends keyof any, T> = {
[P in K]: T;
};
// 类型守卫泛型
function isOfType<T>(value: any, key: keyof T): value is T {
return value && typeof value === "object" && key in value;
}
// 泛型工厂函数
function createInstance<T>(ctor: new (...args: any[]) => T, ...args: any[]): T {
return new ctor(...args);
}
// 链式调用泛型
class QueryBuilder<T> {
private filters: ((item: T) => boolean)[] = [];
where(predicate: (item: T) => boolean): this {
this.filters.push(predicate);
return this;
}
execute(items: T[]): T[] {
return items.filter((item) => this.filters.every((filter) => filter(item)));
}
}
// 递归泛型类型
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 泛型约束示例
interface DatabaseEntity {
id: string;
createdAt: Date;
updatedAt: Date;
}
interface UserEntity extends DatabaseEntity {
name: string;
email: string;
}
interface Repository<T extends DatabaseEntity> {
findById(id: string): Promise<T | null>;
findAll(): Promise<T[]>;
create(entity: Omit<T, keyof DatabaseEntity>): Promise<T>;
update(id: string, updates: Partial<T>): Promise<T | null>;
delete(id: string): Promise<boolean>;
}
class UserRepository implements Repository<UserEntity> {
private users: UserEntity[] = [];
async findById(id: string): Promise<UserEntity | null> {
return this.users.find((user) => user.id === id) || null;
}
async findAll(): Promise<UserEntity[]> {
return [...this.users];
}
async create(
data: Omit<UserEntity, keyof DatabaseEntity>,
): Promise<UserEntity> {
const user: UserEntity = {
...data,
id: Math.random().toString(36).substr(2, 9),
createdAt: new Date(),
updatedAt: new Date(),
};
this.users.push(user);
return user;
}
async update(
id: string,
updates: Partial<UserEntity>,
): Promise<UserEntity | null> {
const index = this.users.findIndex((user) => user.id === id);
if (index === -1) return null;
this.users[index] = {
...this.users[index],
...updates,
updatedAt: new Date(),
};
return this.users[index];
}
async delete(id: string): Promise<boolean> {
const initialLength = this.users.length;
this.users = this.users.filter((user) => user.id !== id);
return this.users.length < initialLength;
}
}4.3 泛型在实际项目中的应用
typescript
// API 客户端泛型
interface ApiConfig {
baseURL: string;
timeout: number;
headers: Record<string, string>;
}
class ApiClient {
constructor(private config: ApiConfig) {}
async get<T = any>(
endpoint: string,
params?: Record<string, any>,
): Promise<T> {
const url = new URL(endpoint, this.config.baseURL);
if (params) {
Object.keys(params).forEach((key) =>
url.searchParams.append(key, params[key]),
);
}
const response = await fetch(url.toString(), {
method: "GET",
headers: this.config.headers,
signal: AbortSignal.timeout(this.config.timeout),
});
if (!response.ok) {
throw new Error(`API Error: ${response.statusText}`);
}
return response.json();
}
async post<T = any, D = any>(endpoint: string, data: D): Promise<T> {
const response = await fetch(
new URL(endpoint, this.config.baseURL).toString(),
{
method: "POST",
headers: {
"Content-Type": "application/json",
...this.config.headers,
},
body: JSON.stringify(data),
signal: AbortSignal.timeout(this.config.timeout),
},
);
if (!response.ok) {
throw new Error(`API Error: ${response.statusText}`);
}
return response.json();
}
}
// 状态管理泛型
interface Action<T extends string = string> {
type: T;
}
interface PayloadAction<T extends string, P> extends Action<T> {
payload: P;
}
type ActionCreator<T extends string = string> = () => Action<T>;
type PayloadActionCreator<T extends string = string, P = any> = (
payload: P,
) => PayloadAction<T, P>;
function createAction<T extends string>(type: T): ActionCreator<T>;
function createAction<T extends string, P>(type: T): PayloadActionCreator<T, P>;
function createAction<T extends string, P>(type: T) {
return (payload?: P) =>
payload === undefined ? { type } : { type, payload };
}
// Reducer 泛型
type Reducer<S = any, A extends Action = Action> = (state: S, action: A) => S;
function createReducer<S, A extends Action = Action>(
initialState: S,
handlers: {
[K in A["type"]]?: Reducer<S, Extract<A, { type: K }>>;
},
): Reducer<S, A> {
return (state: S = initialState, action: A) => {
const handler = handlers[action.type as A["type"]];
return handler ? handler(state, action as any) : state;
};
}
// 表单处理泛型
interface FormField<T = any> {
value: T;
error?: string;
touched: boolean;
valid: boolean;
}
interface FormConfig<T extends Record<string, any>> {
initialValues: T;
validate?: (values: T) => Partial<Record<keyof T, string>>;
onSubmit: (values: T) => void | Promise<void>;
}
class FormHandler<T extends Record<string, any>> {
private values: T;
private errors: Partial<Record<keyof T, string>> = {};
private touched: Set<keyof T> = new Set();
constructor(private config: FormConfig<T>) {
this.values = { ...config.initialValues };
}
getField<K extends keyof T>(name: K): FormField<T[K]> {
return {
value: this.values[name],
error: this.errors[name],
touched: this.touched.has(name),
valid: !this.errors[name],
};
}
setFieldValue<K extends keyof T>(name: K, value: T[K]): void {
this.values[name] = value;
this.validateField(name);
}
setFieldTouched<K extends keyof T>(name: K): void {
this.touched.add(name);
this.validateField(name);
}
private validateField<K extends keyof T>(name: K): void {
if (this.config.validate) {
const newErrors = this.config.validate(this.values);
this.errors[name] = newErrors[name];
}
}
async submit(): Promise<void> {
if (this.config.validate) {
this.errors = this.config.validate(this.values);
}
const isValid = Object.keys(this.errors).length === 0;
if (isValid) {
await this.config.onSubmit(this.values);
}
}
reset(): void {
this.values = { ...this.config.initialValues };
this.errors = {};
this.touched.clear();
}
}
// 使用示例
interface LoginFormValues {
email: string;
password: string;
rememberMe: boolean;
}
const loginForm = new FormHandler<LoginFormValues>({
initialValues: {
email: "",
password: "",
rememberMe: false,
},
validate: (values) => {
const errors: Partial<Record<keyof LoginFormValues, string>> = {};
if (!values.email) {
errors.email = "Email is required";
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
errors.email = "Email is invalid";
}
if (!values.password) {
errors.password = "Password is required";
} else if (values.password.length < 6) {
errors.password = "Password must be at least 6 characters";
}
return errors;
},
onSubmit: async (values) => {
console.log("Submitting form:", values);
// API 调用逻辑
},
});
// 使用表单
loginForm.setFieldValue("email", "test@example.com");
loginForm.setFieldValue("password", "password123");
loginForm.setFieldTouched("email");
console.log(loginForm.getField("email"));