Skip to content

类基础

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.14159
console.log(MathUtils.square(4)) // 16
console.log(MathUtils.circleArea(5)) // 78.53975

Getter 和 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 = 25
console.log(temp.fahrenheit) // 77
temp.fahrenheit = 68
console.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/Setterget prop() / set prop()计算属性
this 类型: this链式调用支持

下一篇我们将学习类的访问修饰符:public、private、protected、readonly 和 static。