TypeScript 对 ES6 的类语法进行了增强,添加了类型注解、访问修饰符等特性。这篇文章将介绍 TypeScript 类的基础用法。
类的基本结构#
// TypeScript 5.x
class Person { // 属性声明(必须声明类型) name: string age: number
// 构造函数 constructor(name: string, age: number) { this.name = name this.age = age }
// 方法 greet(): string { return `你好,我是 ${this.name},今年 ${this.age} 岁` }}
const person = new Person('张三', 25)console.log(person.greet()) // 你好,我是 张三,今年 25 岁属性声明#
必须初始化#
TypeScript 严格模式下,属性必须在声明时或构造函数中初始化:
class User { name: string // ❌ 严格模式下报错:未初始化 age: number = 0 // ✅ 声明时初始化
constructor(name: string) { this.name = name // ✅ 构造函数中初始化 }}可选属性#
class Config { host: string port: number timeout?: number // 可选属性
constructor(host: string, port: number, timeout?: number) { this.host = host this.port = port this.timeout = timeout }}
const config = new Config('localhost', 3000)// config.timeout 的类型是 number | undefined只读属性#
class Circle { readonly PI = 3.14159 readonly radius: number
constructor(radius: number) { this.radius = radius // 只能在构造函数中赋值 }
getArea(): number { return this.PI * this.radius ** 2 }}
const circle = new Circle(10)// circle.radius = 20; // ❌ 错误:只读属性参数属性(简写语法)#
// 传统写法class Person1 { name: string age: number
constructor(name: string, age: number) { this.name = name this.age = age }}
// 参数属性简写class Person2 { constructor( public name: string, public age: number ) { // 不需要额外赋值 }}
// 两者效果相同const p1 = new Person1('张三', 25)const p2 = new Person2('张三', 25)方法#
实例方法#
class Calculator { private value: number = 0
add(n: number): this { this.value += n return this // 返回 this 支持链式调用 }
subtract(n: number): this { this.value -= n return this }
multiply(n: number): this { this.value *= n return this }
getResult(): number { return this.value }}
const result = new Calculator().add(10).subtract(3).multiply(2).getResult()console.log(result) // 14静态方法#
class MathUtils { static PI = 3.14159
static square(n: number): number { return n * n }
static cube(n: number): number { return n * n * n }
static circleArea(radius: number): number { return MathUtils.PI * MathUtils.square(radius) }}
// 通过类名调用console.log(MathUtils.PI) // 3.14159console.log(MathUtils.square(4)) // 16console.log(MathUtils.circleArea(5)) // 78.53975Getter 和 Setter#
class Temperature { private _celsius: number = 0
// Getter get celsius(): number { return this._celsius }
// Setter set celsius(value: number) { if (value < -273.15) { throw new Error('温度不能低于绝对零度') } this._celsius = value }
// 计算属性 get fahrenheit(): number { return this._celsius * 1.8 + 32 }
set fahrenheit(value: number) { this._celsius = (value - 32) / 1.8 }}
const temp = new Temperature()temp.celsius = 25console.log(temp.fahrenheit) // 77
temp.fahrenheit = 68console.log(temp.celsius) // 20构造函数#
基本构造函数#
class User { name: string email: string createdAt: Date
constructor(name: string, email: string) { this.name = name this.email = email this.createdAt = new Date() }}可选参数和默认值#
class HttpClient { baseUrl: string timeout: number headers: Record<string, string>
constructor( baseUrl: string, timeout: number = 5000, headers: Record<string, string> = {} ) { this.baseUrl = baseUrl this.timeout = timeout this.headers = headers }}
const client1 = new HttpClient('https://api.example.com')const client2 = new HttpClient('https://api.example.com', 10000)const client3 = new HttpClient('https://api.example.com', 10000, { Authorization: 'Bearer token',})构造函数重载#
class Point { x: number y: number
// 重载签名 constructor(x: number, y: number) constructor(coords: { x: number; y: number }) constructor(coords: [number, number])
// 实现签名 constructor( xOrCoords: number | { x: number; y: number } | [number, number], y?: number ) { if (typeof xOrCoords === 'number') { this.x = xOrCoords this.y = y! } else if (Array.isArray(xOrCoords)) { this.x = xOrCoords[0] this.y = xOrCoords[1] } else { this.x = xOrCoords.x this.y = xOrCoords.y } }}
const p1 = new Point(1, 2)const p2 = new Point({ x: 1, y: 2 })const p3 = new Point([1, 2])this 类型#
this 作为返回类型#
class Builder { private data: Record<string, unknown> = {}
set(key: string, value: unknown): this { this.data[key] = value return this }
build(): Record<string, unknown> { return { ...this.data } }}
// 继承时 this 指向子类class AdvancedBuilder extends Builder { setMultiple(entries: [string, unknown][]): this { entries.forEach(([key, value]) => this.set(key, value)) return this }}
const result = new AdvancedBuilder() .set('name', '张三') .set('age', 25) .setMultiple([ ['city', '北京'], ['job', '工程师'], ]) .build()this 参数#
class Handler { private name = 'Handler'
// 显式声明 this 类型 handle(this: Handler, event: string): void { console.log(`${this.name} handling ${event}`) }}
const handler = new Handler()handler.handle('click') // ✅ 正确
// const fn = handler.handle;// fn('click'); // ❌ 错误:this 上下文不正确this 类型保护#
class FileSystemObject { isFile(): this is FileRep { return this instanceof FileRep }
isDirectory(): this is Directory { return this instanceof Directory }}
class FileRep extends FileSystemObject { content: string = ''}
class Directory extends FileSystemObject { children: FileSystemObject[] = []}
function process(fso: FileSystemObject) { if (fso.isFile()) { // fso: FileRep console.log(fso.content) } else if (fso.isDirectory()) { // fso: Directory console.log(fso.children.length) }}类表达式#
// 类表达式const Rectangle = class { constructor( public width: number, public height: number ) {}
getArea(): number { return this.width * this.height }}
const rect = new Rectangle(10, 20)console.log(rect.getArea()) // 200
// 命名类表达式const NamedClass = class MyClass { static className = 'MyClass'
getClassName(): string { return MyClass.className }}类型推断#
// TypeScript 会推断类的类型class Auto { make = 'Toyota' // 推断为 string year = 2024 // 推断为 number features = ['GPS', 'Bluetooth'] // 推断为 string[]
start() { return 'Engine started' // 返回类型推断为 string }}
// 类本身也是一个类型function createAuto(): Auto { return new Auto()}
// typeof 获取类的构造函数类型type AutoConstructor = typeof Auto// new () => Auto常见问题#
🙋 属性初始化顺序是什么?#
class Example { a = 1 // 1. 属性初始化器 b: number
constructor() { // 2. 构造函数体 console.log(this.a) // 1 this.b = 2 }}
// 初始化器在构造函数体之前执行🙋 如何声明但不初始化属性?#
使用 ! 断言:
class Component { element!: HTMLElement // 告诉 TS 稍后会初始化
mount(container: HTMLElement) { this.element = document.createElement('div') container.appendChild(this.element) }}🙋 类属性可以是函数吗?#
可以,有两种方式:
class Handler { // 方法(原型上) method1() { console.log('method1') }
// 属性(实例上,每个实例一份) method2 = () => { console.log('method2') }
// 箭头函数属性会自动绑定 this}
const h = new Handler()const fn1 = h.method1 // this 会丢失const fn2 = h.method2 // this 被绑定总结#
| 特性 | 语法 | 用途 |
|---|---|---|
| 属性声明 | prop: Type | 声明实例属性 |
| 参数属性 | constructor(public prop: Type) | 简化属性声明 |
| 只读属性 | readonly prop: Type | 不可修改的属性 |
| 静态成员 | static prop / static method() | 类级别的成员 |
| Getter/Setter | get prop() / set prop() | 计算属性 |
| this 类型 | : this | 链式调用支持 |
下一篇我们将学习类的访问修饰符:public、private、protected、readonly 和 static。