类、继承、抽象、访问控制与组合
TypeScript 类建立在 JavaScript 类和原型链之上,并增加静态类型语法。它与 Java/C++ 类表面相似,但运行时模型不同。
1. 字段、构造器与参数属性
class Account {
static readonly kind = "account"
readonly createdAt = new Date()
constructor(
public readonly id: string,
private balanceInCents: number,
) {}
deposit(amountInCents: number): void {
if (!Number.isInteger(amountInCents) || amountInCents <= 0) {
throw new RangeError("amount must be a positive integer")
}
this.balanceInCents += amountInCents
}
get balance(): number {
return this.balanceInCents / 100
}
}
参数属性是 TypeScript 简写,会生成实例字段赋值。private 是 TypeScript 可见性约束;JavaScript #balance 才是由运行时强制的私有字段。
2. TypeScript private 与 JavaScript #private
class Vault {
#token: string
constructor(token: string) {
this.#token = token
}
matches(candidate: string): boolean {
return this.#token === candidate
}
}
| 特性 | private token |
#token |
|---|---|---|
| 类型检查阶段限制 | 是 | 是 |
| 运行时真正不可从类外访问 | 否,普通属性仍存在 | 是,语言级私有槽 |
| 目标较旧 JS 时 | 普通属性 | 编译器可能用辅助结构降级 |
3. 继承与覆写
abstract class Formatter {
abstract format(value: unknown): string
print(value: unknown): void {
console.log(this.format(value))
}
}
class JsonFormatter extends Formatter {
override format(value: unknown): string {
return JSON.stringify(value, null, 2)
}
}
建议开启 noImplicitOverride,让重写意图显式。抽象类在运行时仍是类,但 abstract 限制主要由 TypeScript 检查。
4. 静态侧与实例侧
interface ClockInstance {
now(): Date
}
interface ClockConstructor {
new (): ClockInstance
readonly timezone: string
}
function installClock(Clock: ClockConstructor): ClockInstance {
console.log(Clock.timezone)
return new Clock()
}
implements 检查实例侧,不自动检查构造器与静态成员。需要静态契约时单独描述构造签名。
5. 初始化顺序陷阱
派生类初始化大致经历:基类字段、基类构造器、派生类字段、派生类构造器。基类构造期间调用可覆写方法可能访问尚未初始化的派生字段,因此构造器应避免调用多态方法。
class Base {
constructor() {
// 避免在这里调用 this.render()
}
}
6. 组合优先于深继承
interface Logger {
log(message: string): void
}
class UserService {
constructor(private readonly logger: Logger) {}
create(name: string): void {
this.logger.log(`create:${name}`)
}
}
组合让依赖契约更小、测试替身更容易,避免脆弱基类和继承层级耦合。继承适用于明确“is-a”关系与稳定替换原则,不应用于简单复用几行实现。
7. this 类型与流式 API
class QueryBuilder {
protected clauses: string[] = []
where(expression: string): this {
this.clauses.push(`WHERE ${expression}`)
return this
}
}
class UserQuery extends QueryBuilder {
active(): this {
return this.where("active = true")
}
}
多态 this 保留派生类链式调用类型。
8. 与 Java/C++ 的关键差异
- JavaScript/TypeScript 使用原型链,类语法不改变这一底层事实。
- TypeScript 默认结构兼容;Java/C++ 类类型通常以声明身份和继承关系为核心。
- TypeScript 没有 C++ 的析构函数、确定性 RAII、对象值语义和多继承实现模型。
- TypeScript
protected/private多为静态限制;#private是 JavaScript 运行时机制。
参考:Classes、MDN Classes。