readonly修饰符用于创建只读属性,属性只能在声明时或构造函数中赋值,之后无法修改。这有助于防止意外修改,保证数据的不可变性。
在属性前添加 readonly 关键字,该属性就变成只读的。
class Config {
readonly appName: string = "我的应用"
readonly version: string
constructor(version: string) {
this.version = version
}
}
const config = new Config("1.0.0")
console.log(config.appName)
console.log(config.version)
appName 在声明时初始化,version 在构造函数中赋值。之后尝试修改这两个属性都会报错。
readonly 可以与访问修饰符一起用在构造函数参数上。
class User {
constructor(
readonly id: number,
readonly name: string,
public email: string
) {}
}
const user = new User(1, "张三", "zhangsan@example.com")
console.log(user.id)
console.log(user.name)
console.log(user.email)
user.email = "new@example.com"
id 和 name 是只读属性,email 是可修改的公共属性。这种写法简洁明了,常用于定义不可变的标识属性。
readonly 通常与 private 一起使用,创建对外只读但对内可写的属性。
class Counter {
private _count: number = 0
get count(): number {
return this._count
}
increment(): void {
this._count++
}
reset(): void {
this._count = 0
}
}
const counter = new Counter()
console.log(counter.count)
counter.increment()
counter.increment()
console.log(counter.count)
counter.reset()
console.log(counter.count)
通过私有属性 _count 和公共存取器 count,实现了对外只读的效果。类内部的方法可以修改 _count,但外部只能读取。
ReadonlyArray 类型用于创建不可变的数组,数组元素不能被修改、添加或删除。
class DataStore {
readonly items: ReadonlyArray<string>
constructor(items: string[]) {
this.items = items
}
addItem(item: string): DataStore {
return new DataStore([...this.items, item])
}
}
const store = new DataStore(["a", "b", "c"])
console.log(store.items)
const newStore = store.addItem("d")
console.log(newStore.items)
items 是只读数组,不能直接修改。addItem 方法返回一个新的 DataStore 实例,保持原实例不变。这种不可变数据模式在函数式编程中很常见。
子类可以继承父类的只读属性,但不能将其改为可写的。
class Entity {
readonly id: number
constructor(id: number) {
this.id = id
}
}
class User extends Entity {
name: string
constructor(id: number, name: string) {
super(id)
this.name = name
}
}
const user = new User(1, "张三")
console.log(user.id)
console.log(user.name)
id 在父类中定义为只读,子类继承后仍然是只读的。子类不能重新定义 id 为可写属性。
接口中的属性也可以使用 readonly 修饰。
interface Point {
readonly x: number
readonly y: number
}
function movePoint(point: Point, dx: number, dy: number): Point {
return {
x: point.x + dx,
y: point.y + dy
}
}
const p1: Point = { x: 10, y: 20 }
const p2 = movePoint(p1, 5, 5)
console.log(p1)
console.log(p2)
接口定义的只读属性在赋值后不能修改。movePoint 函数返回一个新的点对象,而不是修改原来的点。
使用类型别名可以创建只读的对象类型。
type ReadonlyUser = {
readonly id: number
readonly name: string
}
function printUser(user: ReadonlyUser): void {
console.log(`ID: ${user.id}, Name: ${user.name}`)
}
const user: ReadonlyUser = { id: 1, name: "张三" }
printUser(user)
ReadonlyUser 类型的对象创建后,其属性不能被修改。
TypeScript 提供了 Readonly<T> 工具类型,将类型 T 的所有属性变为只读。
interface User {
id: number
name: string
email: string
}
function freezeUser(user: User): Readonly<User> {
return Object.freeze(user)
}
const user: User = {
id: 1,
name: "张三",
email: "zhangsan@example.com"
}
const frozenUser = freezeUser(user)
console.log(frozenUser.id)
Readonly<User> 将 User 的所有属性变为只读,配合 Object.freeze 可以实现真正的不可变对象。
readonly 只作用于属性本身,不会递归应用到嵌套对象。要实现深层只读,需要自定义类型。
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
interface Config {
server: {
host: string
port: number
}
database: {
host: string
name: string
}
}
const config: DeepReadonly<Config> = {
server: {
host: "localhost",
port: 3000
},
database: {
host: "localhost",
name: "mydb"
}
}
console.log(config.server.host)
DeepReadonly 递归地将所有嵌套属性变为只读,确保整个对象树都不可变。
只读修饰符在配置对象、常量定义、不可变数据结构等场景中非常有用。
class Application {
readonly config = {
apiUrl: "https://api.example.com",
timeout: 5000,
maxRetries: 3
}
async fetchData(endpoint: string): Promise<any> {
const url = `${this.config.apiUrl}/${endpoint}`
console.log(`请求 ${url},超时 ${this.config.timeout}ms`)
}
}
const app = new Application()
app.fetchData("users")
配置对象设为只读,确保运行时不会被意外修改,提高了代码的可靠性。
readonly 修饰符用于创建只读属性,防止属性被意外修改。它可以用于类属性、接口属性、数组类型和工具类型。合理使用只读修饰符可以提高代码的安全性,明确表达数据不可变的意图,减少因意外修改导致的 bug。在需要不可变数据的场景中,readonly 是一个非常有用的工具。