Class 类
TypeScript 的类系统建立在 ES6 类的基础之上,通过访问修饰符、构造函数简写、接口实现等特性,为面向对象编程提供了强大的类型支持。
类定义
TypeScript 中的类是 ES6 类的超集,增加了类型注解和编译时检查能力。
class Person {
name: string = "Tom Mark"; // 默认 public 修饰符
getName(): string {
return this.name;
}
}
const person = new Person();
console.log(person.getName()); // 输出: Tom Mark2
3
4
5
6
7
8
9
语法要点:
- 类名使用帕斯卡命名法(
PascalCase),如Person、UserService - 成员变量可以直接在类中初始化,也可在构造函数中赋值
- 方法默认绑定到实例,通过
this访问实例属性
编译后的:
TypeScript 类编译后会生成标准的 JavaScript 构造函数和原型方法。了解这一点对于理解运行时行为至关重要:
// 编译后的 JavaScript (ES6+)
class Person {
constructor() {
this.name = "Tom Mark";
}
getName() {
return this.name;
}
}2
3
4
5
6
7
8
9
修饰符
访问修饰符
TypeScript 提供四种访问修饰符,控制类成员的可见性
1. public 公开访问
默认修饰符,所有类外部都可以访问
class User {
public id: number = 1; // 显式声明为 public
name: string = "Alice"; // 省略修饰符时默认为 public
}
const user = new User();
console.log(user.id); // ✅ 允许访问
console.log(user.name); // ✅ 允许访问2
3
4
5
6
7
8
使用场景
DTO类的数据属性(需要序列化传输)- 服务类的公共方法(对外
API) - 纯数据对象(
POJO风格)
TIP
尽管 public 是默认值,但在团队协作中显式声明可以明确设计意图,提高代码可读性。
2. private 私有访问
仅在类内部可以访问,子类也不能访问
class User {
private secret: string = "confidential";
revealSecret(): string {
return this.secret; // ✅ 类内部可访问
}
}
const user = new User();
// console.log(user.secret); // ❌ 编译错误
console.log(user.revealSecret()); // ✅ 通过公共方法间接访问2
3
4
5
6
7
8
9
10
11
关键特性
private成员 = 仅当前类内部可用子类不能访问、不能覆盖父类
private成员类的实例不能访问、不能修改
private成员编译成
JS后没有真正私有(只是TS编译时检查),真正运行时私有用#(ES私有字段)TypeScriptclass SecureUser { #password: string; // 真正的私有字段 private token: string; // 仅编译时私有 constructor(password: string, token: string) { this.#password = password; this.token = token; } validate(input: string): boolean { return input === this.#password; } } const secure = new SecureUser("secret123", "token456"); // secure.#password // ❌ 语法错误,无法访问 // secure["#password"] // ❌ undefined,无法通过任何方式访问 secure.validate("secret123"); // ✅ 通过公共方法验证1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18建议
对于敏感数据(密码、密钥)使用
#语法;对于一般的封装需求使用private
使用场景
隐藏内部状态(私有属性)
类里有不希望被外部直接修改的数据,必须用
private保护。TypeScriptclass User { // 私有:外部不能直接改密码 private password: string; constructor(public name: string, pwd: string) { this.password = pwd; } // 只暴露安全的验证方法,不暴露密码本身 verifyPassword(input: string): boolean { return this.password === input; } } const user = new User("张三", "123456"); console.log(user.name); // ✅ 允许 console.log(user.password); // ❌ 报错:私有属性,外部不能访问 user.password = "xxx"; // ❌ 报错1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18适用:密码、token、内部缓存、状态标记等。
封装内部工具方法(私有方法)
类里有只给内部用的工具函数,不希望外部调用,用
privateTypeScriptclass Calculator { // 私有工具方法:只在类内部用 private checkNumber(n: number): boolean { return !isNaN(n); } add(a: number, b: number): number { // 内部调用私有方法 ✅ if (!this.checkNumber(a) || !this.checkNumber(b)) return NaN; return a + b; } } const calc = new Calculator(); calc.add(1, 2); // ✅ calc.checkNumber(10); // ❌ 报错:私有方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16适用:校验、格式化、日志、数据处理等内部辅助逻辑。
防止子类意外覆盖(安全继承)
private成员子类完全访问不到,能避免子类不小心重写 / 修改父类关键逻辑。TypeScriptclass BaseApi { // 私有:子类绝对无法访问/覆盖 private apiKey = "secret-key"; request() { return `使用密钥:${this.apiKey}`; } } class UserApi extends BaseApi { test() { console.log(this.apiKey); // ❌ 报错:父类私有属性 } }1
2
3
4
5
6
7
8
9
10
11
12
13
14提供受控访问(
getter/setter)私有属性不直接暴露,而是通过
get/set做校验、格式化、日志等安全控制。TypeScriptclass Product { private _price: number; constructor(price: number) { this._price = price; } // 对外暴露可读,但不能直接改 get price(): number { return this._price; } // 受控修改:必须大于0 set price(newPrice: number) { if (newPrice > 0) this._price = newPrice; else throw new Error("价格必须大于0"); } } const p = new Product(100); p.price = -50; // ❌ 抛错 p.price = 200; // ✅ 合法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22适用:需要严格控制修改规则的字段。
单例模式(私有构造函数)
经典场景:限制一个类只能创建一个实例,把 constructor 设为 private。
TypeScriptclass AppConfig { private static instance: AppConfig; // 私有构造:外部不能 new private constructor() {} static getInstance(): AppConfig { if (!this.instance) this.instance = new AppConfig(); return this.instance; } } // 只能通过静态方法获取唯一实例 const config1 = AppConfig.getInstance(); const config2 = AppConfig.getInstance(); console.log(config1 === config2); // true new AppConfig(); // ❌ 报错:构造函数私有1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17适用:配置类、日志类、数据库连接类。
避免命名冲突 / 防止外部误用
类内部有和公共方法同名 / 容易被误用的属性 / 方法时,用
private隔离。TypeScriptclass Logger { // 私有:避免和外部方法冲突 private logCount = 0; public log(message: string) { this.logCount++; console.log(`[${this.logCount}] ${message}`); } } const logger = new Logger(); logger.log("hello"); // ✅ logger.logCount; // ❌ 报错1
2
3
4
5
6
7
8
9
10
11
12
13
3. protected 受保护访问
protected 成员对子类可见,但对外部不可见,是实现继承体系的关键工具
class Animal {
protected name: string;
protected energy: number = 100;
constructor(name: string) {
this.name = name;
}
protected consumeEnergy(amount: number): void {
this.energy = Math.max(0, this.energy - amount);
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
run(distance: number): void {
console.log(`${this.name} is running...`); // ✅ 可访问 protected name
this.consumeEnergy(distance * 0.5); // ✅ 可访问 protected 方法
console.log(`Energy remaining: ${this.energy}`);
}
}
const dog = new Dog("Max", "Golden Retriever");
dog.run(50);
// dog.name; // ❌ 编译错误,外部不可访问
// dog.energy; // ❌ 编译错误2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
使用场景
- 框架/库的基类设计(如
NestJS的Injectable) - 模板方法模式中的钩子方法
- 领域驱动设计中的聚合根基类
4. readonly 只读修饰符
readonly 确保属性只能在声明时或构造函数中赋值,之后不可修改。
class Configuration {
readonly apiKey: string;
readonly version: string = "1.0.0";
readonly maxConnections: number;
constructor(apiKey: string) {
this.apiKey = apiKey; // ✅ 构造函数中可赋值
this.maxConnections = 100; // ✅ 构造函数中可赋值
}
updateApiKey(newKey: string): void {
// this.apiKey = newKey; // ❌ 编译错误
}
}
const config = new Configuration("sk-12345");
console.log(config.apiKey); // ✅ 可读取
// config.apiKey = "new-key"; // ❌ 编译错误2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
组合使用
class User {
public readonly id: number; // 公开且只读
private readonly createdAt: Date; // 私有且只读
protected readonly config: object; // 受保护且只读
constructor(id: number) {
this.id = id;
this.createdAt = new Date();
this.config = { theme: "dark" };
}
}2
3
4
5
6
7
8
9
10
11
场景选择
| 需求场景 | 推荐修饰符 | 示例 |
|---|---|---|
| DTO 数据属性 | public | class CreateUserDto |
| 配置常量 | public readonly | readonly API_VERSION = "v1"; |
| 内部状态 | private | private isLoading = false; |
| 敏感数据 | # (ES 私有) | #apiKey: string; |
| 可被子类重写 | protected | protected validate(): boolean |
| 类级别共享 | static | static count = 0; |
| 单例模式 | private constructor | private constructor() {} |
静态成员与实例成员
class Counter {
// 静态成员:属于类本身
private static totalCount: number = 0;
public static readonly MAX_COUNT: number = 1000;
// 实例成员:属于每个实例
private instanceCount: number = 0;
constructor() {
Counter.totalCount++;
this.instanceCount++;
}
// 静态方法
public static getTotalCount(): number {
return Counter.totalCount;
}
// 实例方法
public getInstanceCount(): number {
return this.instanceCount;
}
}
const c1 = new Counter();
const c2 = new Counter();
console.log(Counter.getTotalCount()); // 2
console.log(c1.getInstanceCount()); // 12
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
类继承机制
extends 关键字
TypeScript 继承基于 ES6 的原型链机制,使用 extends 关键字建立继承关系。
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
private breed: string;
constructor(name: string, breed: string) {
super(name); // 必须先调用 super
this.breed = breed;
}
move(distance: number = 5): void {
console.log(`${this.breed} dog ${this.name} is running...`);
super.move(distance); // 调用父类方法
}
bark(): void {
console.log("Woof! Woof!");
}
}
const dog = new Dog("Max", "Golden Retriever");
dog.move(10); // Golden Retriever dog Max is running... Max moved 10m.
dog.bark(); // Woof! Woof!2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
继承规则
- 子类继承父类所有
public和protected成员 - 子类不继承父类的
private成员 - 子类不继承父类的静态成员(但可通过
ParentClass.staticMethod()访问) TypeScript不支持多重继承(一个类只能有一个父类)
方法覆盖(Override)
从 TypeScript 4.3 开始,可以使用 override 关键字显式标记方法覆盖,提高代码安全性。
class Base {
greet(name: string): string {
return `Hello, ${name}`;
}
protected getConfig(): object {
return { version: "1.0" };
}
}
class Derived extends Base {
// 使用 override 关键字(推荐)
override greet(name: string): string {
return `Hi there, ${name}! Welcome back.`;
}
// 不使用 override 也可以,但编译器会警告(取决于配置)
protected getConfig(): object {
return { ...super.getConfig(), enhanced: true };
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
启用严格覆盖检查
// tsconfig.json
{
"compilerOptions": {
"noImplicitOverride": true
}
}2
3
4
5
6
启用后,覆盖父类方法必须使用 override 关键字,否则编译错误。
super 关键字
super 在子类中有两种用途:
1. 调用父类构造函数
class Vehicle {
constructor(
public type: string,
protected speed: number = 0
) {
console.log(`Vehicle created: ${type}`);
}
}
class Car extends Vehicle {
constructor(
public brand: string,
speed: number
) {
super("Automobile", speed); // 必须在 this 之前调用
console.log(`Car brand: ${brand}`);
}
accelerate(increment: number): void {
this.speed += increment; // 可访问 protected speed
}
}
const car = new Car("Toyota", 60);
// 输出顺序:
// Vehicle created: Automobile
// Car brand: Toyota2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
关键规则
- 子类构造函数必须调用
super(),且必须在访问this之前 - 如果子类没有显式定义构造函数,会自动调用父类的无参构造函数
super()的参数必须与父类构造函数签名匹配
2. 调用父类方法
class Printer {
print(message: string): void {
console.log(`[Printer] ${message}`);
}
}
class ColorPrinter extends Printer {
private color: string;
constructor(color: string = "black") {
super();
this.color = color;
}
override print(message: string): void {
super.print(message); // 调用父类实现
console.log(`[ColorPrinter] Color: ${this.color}`);
}
}
const printer = new ColorPrinter("blue");
printer.print("Hello World");
// [Printer] Hello World
// [ColorPrinter] Color: blue2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
继承初始化顺序
理解初始化顺序对于调试复杂的继承体系至关重要。
class Parent {
static staticField = console.log("1. 父类静态字段");
instanceField = console.log("3. 父类实例字段");
constructor() {
console.log("4. 父类构造函数");
}
}
class Child extends Parent {
static staticField = console.log("2. 子类静态字段");
instanceField = console.log("5. 子类实例字段");
constructor() {
console.log("(super 调用前)"); // ❌ 不能在 super 前访问 this
super();
console.log("6. 子类构造函数");
}
}
console.log("--- 创建实例 ---");
new Child();
// 执行顺序:
// 1. 父类静态字段
// 2. 子类静态字段
// 3. 父类实例字段
// 4. 父类构造函数
// 5. 子类实例字段
// 6. 子类构造函数2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
构造函数
TypeScript 中的构造函数是用于创建和初始化对象的特殊方法。每个类都有一个默认的构造函数,也可以自定义构造函数。
参数属性简写(Parameter Properties)
TypeScript 提供了参数属性语法糖,可以在构造函数参数中同时声明和初始化类成员。
// 传统写法(冗长)
class UserTraditional {
public id: string;
public name: string;
private email: string;
protected role: string;
constructor(id: string, name: string, email: string, role: string) {
this.id = id;
this.name = name;
this.email = email;
this.role = role;
}
}
// 参数属性写法(简洁)
class UserConcise {
constructor(
public id: string,
public name: string,
private email: string,
protected role: string = "user"
) {}
}
// 两者编译后的 JavaScript 代码完全相同2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
支持的修饰符:
publicprivateprotectedreadonly- 组合使用:
public readonly、private readonly
常见陷阱:重复赋值
// ❌ 错误:导致重复赋值
class Bad {
constructor(public name: string) {
this.name = name; // 冗余!TypeScript 已经自动完成了
}
}
// 编译后的 JS:
class Bad {
constructor(name) {
this.name = name; // 参数属性自动生成
this.name = name; // 你的手动赋值(重复!)
}
}
// ✅ 正确:让 TypeScript 处理
class Good {
constructor(
public name: string,
private age: number
) {
// 不需要手动赋值
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
构造函数重载
TypeScript 支持构造函数重载,允许以多种方式实例化类。
class Point {
public x: number;
public y: number;
// 重载签名
constructor();
constructor(x: number, y: number);
constructor(point: { x: number; y: number });
// 实现签名
constructor(xOrPoint?: number | { x: number; y: number }, y?: number) {
if (typeof xOrPoint === "object") {
this.x = xOrPoint.x;
this.y = xOrPoint.y;
} else {
this.x = xOrPoint ?? 0;
this.y = y ?? 0;
}
}
}
const p1 = new Point(); // (0, 0)
const p2 = new Point(10, 20); // (10, 20)
const p3 = new Point({ x: 5, y: 8 }); // (5, 8)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
私有构造函数与单例模式
class Singleton {
private static instance: Singleton;
// 私有构造函数:外部无法使用 new
private constructor(
public readonly name: string
) {}
// 全局访问点
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton("default");
}
return Singleton.instance;
}
}
// const s1 = new Singleton("test"); // ❌ 编译错误
const s2 = Singleton.getInstance(); // ✅ 唯一访问方式2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
其他应用场景
- 工厂模式(通过静态方法创建实例)
Builder模式(强制使用Builder类创建)- 防止类被继承(配合
final语义)
异步初始化模式
构造函数不能是 async 的,但可以通过以下模式实现异步初始化:
// 模式 1:静态工厂方法
class DatabaseConnection {
private constructor(private connection: any) {}
static async create(connectionString: string): Promise<DatabaseConnection> {
const conn = await connectToDatabase(connectionString);
return new DatabaseConnection(conn);
}
}
// 使用
const db = await DatabaseConnection.create("mongodb://localhost");
// 模式 2:初始化方法
class AsyncResource {
private initialized = false;
constructor(public config: object) {
// 仅同步初始化
}
async init(): Promise<void> {
if (this.initialized) return;
await this.loadResources();
this.initialized = true;
}
private async loadResources(): Promise<void> {
// 异步加载资源
}
}
// 使用
const resource = new AsyncResource({ url: "..." });
await resource.init();
// 模式 3:Promise 属性
class DataLoader {
readonly data: Promise<string[]>;
constructor(url: string) {
this.data = fetch(url).then(r => r.json());
}
}
// 使用
const loader = new DataLoader("/api/data");
const data = await loader.data;2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
类实现接口
implements 关键字
implements 关键字用于声明类满足某个接口的契约。
interface Loggable {
log(message: string): void;
readonly prefix?: string;
}
interface Serializable {
serialize(): string;
}
// 单接口实现
class ConsoleLogger implements Loggable {
readonly prefix = "[LOG]";
log(message: string): void {
console.log(`${this.prefix} ${message}`);
}
}
// 多接口实现
class JsonLogger implements Loggable, Serializable {
readonly prefix = "[JSON]";
log(message: string): void {
console.log(JSON.stringify({ level: "info", message }));
}
serialize(): string {
return JSON.stringify({ prefix: this.prefix });
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
实现规则
- 必须实现接口中所有必需成员
- 可选成员可以不实现
- 实现的方法必须是
public(接口定义的是公共契约) - 可以添加接口中未定义的额外成员
接口与抽象类的选择
| 特性 | Interface + implements | Abstract Class + extends |
|---|---|---|
| 实现继承 | ❌ 无 | ✅ 有 |
| 多继承/实现 | ✅ 多接口 | ❌ 单继承 |
| 运行时影响 | ❌ 编译后消失 | ✅ 保留 |
| 装饰器支持 | ❌ 不支持 | ✅ 支持 |
| 默认实现 | ❌ 无(TypeScript 5.x 前) | ✅ 可有 |
| 适用场景 | 定义契约 | 共享实现 |
// 接口:定义契约
interface IRepository<T> {
findById(id: string): Promise<T | null>;
save(entity: T): Promise<void>;
}
// 抽象类:提供部分实现
abstract class BaseRepository<T> implements IRepository<T> {
constructor(protected tableName: string) {}
// 通用实现
async findById(id: string): Promise<T | null> {
const query = `SELECT * FROM ${this.tableName} WHERE id = ?`;
return this.executeQuery(query, [id]);
}
// 子类必须实现
abstract save(entity: T): Promise<void>;
// 辅助方法
protected abstract executeQuery(sql: string, params: any[]): Promise<T | null>;
}
// 具体实现
class UserRepository extends BaseRepository<User> {
constructor() {
super("users");
}
async save(user: User): Promise<void> {
// 具体实现
}
protected async executeQuery(sql: string, params: any[]): Promise<User | null> {
// 数据库执行逻辑
return null;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
泛型接口实现
interface Repository<T, ID = string> {
findById(id: ID): Promise<T | null>;
findAll(): Promise<T[]>;
save(entity: T): Promise<T>;
delete(id: ID): Promise<void>;
}
interface Identifiable<ID = string> {
id: ID;
}
class InMemoryRepository<T extends Identifiable> implements Repository<T> {
private items: Map<string, T> = new Map();
async findById(id: string): Promise<T | null> {
return this.items.get(id) ?? null;
}
async findAll(): Promise<T[]> {
return Array.from(this.items.values());
}
async save(entity: T): Promise<T> {
this.items.set(entity.id, entity);
return entity;
}
async delete(id: string): Promise<void> {
this.items.delete(id);
}
}
// 使用
interface User extends Identifiable {
id: string;
name: string;
email: string;
}
const userRepo = new InMemoryRepository<User>();
await userRepo.save({ id: "1", name: "Alice", email: "alice@example.com" });2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
接口继承类
TypeScript 允许接口继承类,这是一种强大但容易被误解的特性。
基本语法
class Control {
private state: any;
protected config: object = {};
public id: string = "default";
setState(newState: any): void {
this.state = newState;
}
}
// 接口继承类
interface SelectableControl extends Control {
select(): void;
}2
3
4
5
6
7
8
9
10
11
12
13
14
继承内容
- 接口继承类的所有成员的类型定义(包括
private和protected) - 不继承具体实现逻辑
关键限制
由于接口继承了私有成员的类型定义,实现该接口的类必须满足私有成员的来源约束。
// ❌ 错误:无法直接实现
class BadSelectable implements SelectableControl {
select(): void { /* ... */ }
// Error: 类型 'BadSelectable' 缺少属性 'state',但必需
}
// ✅ 正确:通过继承原始类
class GoodSelectable extends Control implements SelectableControl {
select(): void {
console.log("Selected:", this.id); // 可访问 public id
// this.state = {}; // ❌ 仍无法访问 private state
this.config = { theme: "dark" }; // ✅ 可访问 protected config
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
规则总结
- 如果接口继承了包含
private成员的类,则只有该类或其子类才能实现该接口 protected成员在子类中可访问private成员即使在子类中也无法直接访问
实际应用场景
扩展第三方组件
// 假设这是第三方库的类
class BaseComponent {
private internalState: any;
protected props: object = {};
render(): JSX.Element {
return "<div>Base</div>";
}
}
// 我们想创建一个可选择的组件
interface SelectableComponent extends BaseComponent {
onSelect(): void;
isSelected: boolean;
}
// 正确的实现方式
class CustomSelectable extends BaseComponent implements SelectableComponent {
isSelected = false;
onSelect(): void {
this.isSelected = !this.isSelected;
// this.internalState = {}; // ❌ 无法访问
this.props = { selected: this.isSelected }; // ✅ 可访问
}
override render(): JSX.Element {
return `<div class="${this.isSelected ? 'selected' : ''}">Custom</div>`;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

