怎么对比 as const 和 readonly 在类型窄化中的区别?

文章导读
在 TypeScript 中,若想实现字面量类型窄化或深层不可变,优先选 as const;若仅需限制属性重新赋值,使用 readonly 修饰符即可。
📋 目录
  1. 核心用法与场景
  2. 原理简述
  3. 在线验证与测试
  4. 常见坑
  5. 参考来源
A A

在 TypeScript 中,若想实现字面量类型窄化或深层不可变,优先选 as const;若仅需限制属性重新赋值,使用 readonly 修饰符即可。

先说结论:as const 侧重于值类型的字面量推断与深层冻结,readonly 侧重于对象属性的赋值权限控制。

  • 适合:需要数组元素或对象属性值为具体字面量类型(如 'red' 而非 string)时使用 as const。
  • 重点看:readonly 默认只保护当前层级,嵌套对象内部属性仍可能被修改。
  • 别忽略:as const 会递归添加 readonly,导致整个结构无法变更,适合配置常量。

核心用法与场景

根据你需要保护的粒度选择语法:

// 场景 1:只需防止属性被重新赋值
interface User { readonly id: string; }

// 场景 2:需要字面量类型 + 深层不可变
const config = { db: { host: "localhost" } } as const;

1. 检查数组类型推断:如果直接定义数组,类型通常是宽泛的 number[] 或 string[]。若需要元素类型为字面量,需添加 as const。

怎么对比 as const 和 readonly 在类型窄化中的区别?
const colors = ["red", "green"] as const;
// 类型推断为:readonly ["red", "green"]

2. 检查嵌套对象可变性:如果配置对象有多层结构,使用 readonly 修饰符可能无法保护内层属性。

interface Config { readonly db: { host: string; }; }
const config: Config = { db: { host: "localhost" } };
config.db.host = "127.0.0.1"; // ✅ 允许修改嵌套属性

3. 应用深层冻结:改用 as const 可锁死所有层级。

const config = { db: { host: "localhost" } } as const;
config.db.host = "127.0.0.1"; // ❌ 编译错误

原理简述

两者在类型系统层面的根本定位不同。readonly 是属性访问修饰符,本质是对属性访问权限的标记,类似于 public 或 private,它仅限制该属性不可被重新赋值,但不改变类型本身的推断逻辑。而 as const 是字面量类型断言,强制 TypeScript 将值的类型推断为最精确的字面量类型,并递归添加 readonly 修饰。

怎么对比 as const 和 readonly 在类型窄化中的区别?

在线验证与测试

除了本地 IDE 悬浮查看类型外,建议使用 TypeScript Playground 进行在线交互验证,确保行为符合预期。

验证步骤:

  1. 访问 TypeScript Playground
  2. 将上述代码片段粘贴到编辑器中。
  3. 观察左侧类型提示,或尝试修改属性查看报错信息。

若显示为字面量联合类型(如 "red" | "green")或包含 readonly 标记的元组,说明 as const 生效。尝试修改属性或数组 push 操作,若编辑器报错提示“无法分配到只读属性”或“方法不存在”,则保护机制已生效。

常见坑

  1. readonly 的局限性:仅作用于当前层级,嵌套对象的属性默认仍可变,不要误以为加了 readonly 就万事大吉。
  2. 数组方法丢失:使用 as const 后的数组变为只读元组,push、splice 等修改方法将不可用,编译时会报错。
  3. 类型过窄:as const 会将类型锁死为字面量,若后续需要作为普通 string 或 number 使用,可能需要额外的类型转换。

参考来源