🚀 TypeScript中的readonly与const的区别是什么

📅 发布于 2026年1月 | 👤 作者:博主 | 🏷️ 标签:TypeScript, readonly, const, as const, 类型安全, Web开发, 前端, 面试

欢迎来到我的博客文章!所有文章都是满满的前端干货,文章简明扼要。

在 TypeScript 中,constreadonlyas const(const 断言) 都表示"只读",但它们的作用域、使用场景和行为完全不同。

📊 三者对比总览

特性 const readonly as const(const 断言)
作用域 变量声明 属性/参数声明 字面量表达式
影响层面 运行时 + 编译时 仅编译时 仅编译时(类型推断)
能否重新赋值 ❌ 变量引用 ❌ 属性值 -
能否修改内部属性 ✅ 可修改对象内部 ✅ 可修改嵌套对象 ❌ 完全递归只读
生成 JS 代码 ✅ 生成 const ❌ 不生成 ❌ 不生成

🔍 详细解析

1. const:变量级别的常量

const PI = 3.14;
PI = 3.14159; // ❌ 编译错误:Cannot assign to 'PI' because it is a constant

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; // ❌ 编译错误:Cannot assign to 'x' because it is a read-only property
p.y = 10; // ❌ 编译错误

// 但可以重新赋值整个对象
let p2: Point = { x: 1, y: 2 };
p2 = { x: 3, y: 4 }; // ✅ 可以

编译后的 JS:

// readonly 不生成任何代码,仅类型检查
const p = { x: 10, y: 20 };
p.x = 5; // ✅ 运行时不会报错!

✅ 特点

3. as const:字面量的深度只读

const obj = {
  x: 1,
  y: 2,
  nested: { z: 3 }
} as const;

obj.x = 5; // ❌ 编译错误
obj.nested.z = 10; // ❌ 编译错误:nested 也是只读的
obj.nested = {}; // ❌ 编译错误

类型推断效果:

// 没有 as const
const arr1 = [1, 2, 3];
// type: number[]

// 使用 as const
const arr2 = [1, 2, 3] as const;
// type: readonly [1, 2, 3] ← 元组 + 字面量类型 + 只读

✅ 特点

🎯 使用场景对比

场景 1:配置常量

❌ 不推荐:类型太宽泛

const CONFIG = {
  API_URL: 'https://api.example.com',
  TIMEOUT: 5000
};
// type: { API_URL: string; TIMEOUT: number }

✅ 推荐:使用 as const

const CONFIG = {
  API_URL: 'https://api.example.com',
  TIMEOUT: 5000
} as const;
// type: { readonly API_URL: 'https://api.example.com'; readonly TIMEOUT: 5000 }

场景 2:函数参数保护

// 使用 readonly 保护参数
function processPoint(point: { readonly x: number; readonly y: number }) {
  // point.x = 10; // ❌ 编译错误
  console.log(point.x, point.y);
}

const p = { x: 1, y: 2 };
processPoint(p);

场景 3:数组常量

// 没有 as const:类型太宽泛
const COLORS = ['red', 'green', 'blue'];
// type: string[]
COLORS.push('yellow'); // ✅ 可以修改

// 使用 as const:精确类型 + 只读
const COLORS2 = ['red', 'green', 'blue'] as const;
// type: readonly ['red', 'green', 'blue']
COLORS2.push('yellow'); // ❌ 编译错误

场景 4:React Props

// 使用 readonly 保护 props
interface ButtonProps {
  readonly label: string;
  readonly onClick: () => void;
}

function Button({ label, onClick }: ButtonProps) {
  // label = 'new'; // ❌ 编译错误
  return <button onClick={onClick}>{label}</button>;
}

实战对比示例

示例 1:对象修改行为

// 1. const:可以修改内部属性
const obj1 = { x: 1 };
obj1.x = 2; // ✅ 可以
obj1 = { x: 3 }; // ❌ 不可以

// 2. readonly:编译时保护属性
interface ReadonlyObj { readonly x: number }
const obj2: ReadonlyObj = { x: 1 };
obj2.x = 2; // ❌ 编译错误
let obj3: ReadonlyObj = { x: 1 };
obj3 = { x: 3 }; // ✅ 可以重新赋值整个对象

// 3. as const:深度只读
const obj4 = { x: 1, nested: { y: 2 } } as const;
obj4.x = 2; // ❌ 编译错误
obj4.nested.y = 3; // ❌ 编译错误(递归只读)

示例 2:数组行为

// 1. const:可以修改数组内容
const arr1 = [1, 2, 3];
arr1.push(4); // ✅ 可以
arr1[0] = 10; // ✅ 可以
arr1 = []; // ❌ 不可以

// 2. readonly:只读数组类型
const arr2: readonly number[] = [1, 2, 3];
arr2.push(4); // ❌ 编译错误
arr2[0] = 10; // ❌ 编译错误

// 3. as const:元组 + 字面量类型 + 只读
const arr3 = [1, 2, 3] as const;
// type: readonly [1, 2, 3]
arr3.push(4); // ❌ 编译错误
arr3[0] = 10; // ❌ 编译错误

选择指南

场景 推荐使用 原因
声明变量 const 防止变量引用被重新赋值
接口属性 readonly 保护对象属性不被修改
函数参数 readonly 防止函数内部修改参数
配置常量 as const 获得精确的字面量类型 + 深度只读
枚举值数组 as const 推断为元组类型,类型更精确
React Props readonly Props 天然只读,明确标记

核心要点总结

💡 关键知识点

✅ 最佳实践

← 返回首页