🚀 TypeScript中的readonly与const的区别是什么
欢迎来到我的博客文章!所有文章都是满满的前端干货,文章简明扼要。
在 TypeScript 中,const、readonly 和 as const(const 断言) 都表示"只读",但它们的作用域、使用场景和行为完全不同。
📊 三者对比总览
| 特性 | const | readonly | as const(const 断言) |
| 作用域 | 变量声明 | 属性/参数声明 | 字面量表达式 |
| 影响层面 | 运行时 + 编译时 | 仅编译时 | 仅编译时(类型推断) |
| 能否重新赋值 | ❌ 变量引用 | ❌ 属性值 | - |
| 能否修改内部属性 | ✅ 可修改对象内部 | ✅ 可修改嵌套对象 | ❌ 完全递归只读 |
| 生成 JS 代码 | ✅ 生成 const | ❌ 不生成 | ❌ 不生成 |
🔍 详细解析
1. const:变量级别的常量
const PI = 3.14;
PI = 3.14159;
const obj = { x: 1, y: 2 };
obj.x = 10;
obj = {};
编译后的 JS:
const PI = 3.14;
const obj = { x: 1, y: 2 };
obj.x = 10;
✅ 特点
- 运行时和编译时都生效
- 阻止变量引用被重新赋值
- 不阻止对象内部属性的修改
2. readonly:属性级别的只读
interface Point {
readonly x: number;
readonly y: number;
}
const p: Point = { x: 10, y: 20 };
p.x = 5;
p.y = 10;
let p2: Point = { x: 1, y: 2 };
p2 = { x: 3, y: 4 };
编译后的 JS:
const p = { x: 10, y: 20 };
p.x = 5;
✅ 特点
- 仅编译时检查,运行时无保护
- 用于接口、类属性、函数参数
- 不阻止嵌套对象的修改(除非也标记为 readonly)
3. as const:字面量的深度只读
const obj = {
x: 1,
y: 2,
nested: { z: 3 }
} as const;
obj.x = 5;
obj.nested.z = 10;
obj.nested = {};
类型推断效果:
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3] as const;
✅ 特点
- 递归地将整个字面量标记为只读
- 将数组推断为元组类型(tuple)
- 将字面量值推断为字面量类型(literal type)
🎯 使用场景对比
场景 1:配置常量
❌ 不推荐:类型太宽泛
const CONFIG = {
API_URL: 'https://api.example.com',
TIMEOUT: 5000
};
✅ 推荐:使用 as const
const CONFIG = {
API_URL: 'https://api.example.com',
TIMEOUT: 5000
} as const;
场景 2:函数参数保护
function processPoint(point: { readonly x: number; readonly y: number }) {
console.log(point.x, point.y);
}
const p = { x: 1, y: 2 };
processPoint(p);
场景 3:数组常量
const COLORS = ['red', 'green', 'blue'];
COLORS.push('yellow');
const COLORS2 = ['red', 'green', 'blue'] as const;
COLORS2.push('yellow');
场景 4:React Props
interface ButtonProps {
readonly label: string;
readonly onClick: () => void;
}
function Button({ label, onClick }: ButtonProps) {
return <button onClick={onClick}>{label}</button>;
}
实战对比示例
示例 1:对象修改行为
const obj1 = { x: 1 };
obj1.x = 2;
obj1 = { x: 3 };
interface ReadonlyObj { readonly x: number }
const obj2: ReadonlyObj = { x: 1 };
obj2.x = 2;
let obj3: ReadonlyObj = { x: 1 };
obj3 = { x: 3 };
const obj4 = { x: 1, nested: { y: 2 } } as const;
obj4.x = 2;
obj4.nested.y = 3;
示例 2:数组行为
const arr1 = [1, 2, 3];
arr1.push(4);
arr1[0] = 10;
arr1 = [];
const arr2: readonly number[] = [1, 2, 3];
arr2.push(4);
arr2[0] = 10;
const arr3 = [1, 2, 3] as const;
arr3.push(4);
arr3[0] = 10;
选择指南
| 场景 | 推荐使用 | 原因 |
| 声明变量 | const | 防止变量引用被重新赋值 |
| 接口属性 | readonly | 保护对象属性不被修改 |
| 函数参数 | readonly | 防止函数内部修改参数 |
| 配置常量 | as const | 获得精确的字面量类型 + 深度只读 |
| 枚举值数组 | as const | 推断为元组类型,类型更精确 |
| React Props | readonly | Props 天然只读,明确标记 |
核心要点总结
💡 关键知识点
- const:变量级别,运行时 + 编译时生效,阻止引用重新赋值
- readonly:属性级别,仅编译时检查,保护对象属性
- as const:字面量级别,深度只读 + 精确类型推断
- 运行时保护:只有 const 在运行时生效
- 嵌套保护:只有 as const 提供递归只读
✅ 最佳实践
- 变量声明优先使用
const - 接口属性和函数参数使用
readonly - 配置常量和枚举值使用
as const - 需要深度只读时使用
as const - 结合使用以获得最佳类型安全