On this page
readonly修饰符初始化限制
为什么readonly属性必须在声明时或构造函数中初始化?对比const与readonly的差异,说明readonly在接口/类型别名中的运行时不可变性保障
考察点分析
本题主要考察以下核心能力维度:
- TypeScript类型系统理解:准确区分
readonly
修饰符与const
声明的适用场景及设计意图 - 语言特性对比能力:辨析编译时类型检查与运行时行为差异
- 类与接口机制:掌握类属性初始化规则及类型修饰符在接口/类型别名中的表现
具体技术评估点:
readonly
与const
的作用域及可变性差异- 类属性初始化阶段限制的底层逻辑
- 类型擦除对运行时类型约束的影响
- 接口/类型别名中
readonly
的编译时检查特性
技术解析
关键知识点
类型系统约束 > 类初始化机制 > 编译时检查
原理剖析
初始化限制
readonly
属性强制在声明时或构造函数中初始化,这是为确保类实例的不变性契约。类实例化时需完成所有状态初始化,若允许在其他方法修改readonly
属性,会破坏编译器的控制流分析。const与readonly差异
const
:变量级不可变,块级作用域,声明时必须初始化(const a=1
)readonly
:属性级不可变,可在构造函数赋值(class A { readonly a=1 }
)
- 运行时不可变性
TypeScript执行类型擦除,接口/类型中的readonly
仅在编译时检查。运行时仍可通过类型断言或JS原生方法修改值:
interface Person { readonly name: string }
const p = { name: 'Alice' } as Person;
(p as any).name = 'Bob'; // 运行时实际可修改
常见误区
- 误认为
readonly
可替代深不可变数据结构 - 混淆
readonly
与const
的作用对象(属性 vs 变量) - 错误假设接口
readonly
在运行时具有保护能力
问题解答
readonly
属性强制在声明或构造函数初始化,是因类实例状态需在构造阶段完全确定。const
用于变量绑定,而readonly
修饰属性,二者作用维度不同。接口/类型别名中的readonly
仅触发编译时检查,TypeScript通过类型擦除机制,不会在运行时添加保护层,实际值仍可能被修改。
关键差异对比:
特性 | const | readonly |
---|---|---|
作用对象 | 变量 | 类/接口属性 |
初始化时机 | 声明时 | 声明时或构造器 |
运行时保护 | 有(JS约束) | 无 |
类型系统层级 | 值空间 | 类型空间 |
解决方案
运行时不可变实现
class User {
// 声明时初始化
readonly id: string = "default";
// 构造器初始化
readonly createdAt: Date;
constructor() {
this.createdAt = new Date();
}
}
// 尝试修改将引发编译错误
const user = new User();
user.id = "new"; // Error: Cannot assign to 'id'
深度不可变方案
// 使用as const实现嵌套只读
const deepReadonlyObj = {
prop: { key: 'value' }
} as const;
// deepReadonlyObj.prop.key = 'new'; // 编译错误
深度追问
如何实现真正运行时只读?
- 使用
Object.freeze()
冻结对象,但仅浅层冻结
类型别名与接口中readonly的行为差异?
- 类型别名中
readonly
可修饰索引签名,接口中不可重复声明属性
只读数组类型如何工作?
ReadonlyArray<T>
移除所有变异方法(如push),编译时阻止修改
Last updated 06 Mar 2025, 13:07 +0800 .