TypeScript 中的 const 主要用于声明一个不能被重新赋值的变量。
不过,TypeScript 中有三个容易混淆的概念:
const:声明常量,变量不能重新赋值。as const:常量断言,让类型更加精确,并添加只读约束。readonly:限制对象属性不能被修改。
下面通过例子说明。
1. const 基础用法
const name = "张三";
const age = 18;
const isAdmin = true;
console.log(name); // 张三
name = "李四"; // ❌ 报错
因为 const 声明的变量不能重新赋值。
对比 let:
let name = "张三";
name = "李四"; // ✅ 可以修改
2. const 也可以指定类型
const age: number = 18;
const name: string = "张三";
const isAdmin: boolean = true;
但 TypeScript 有类型推断,所以通常不需要显式声明。
const age = 18;
TypeScript 会自动推断 age 的类型是字面量类型 18,而不是普通的 number。
对比:
let age = 18; // number
const age2 = 18; // 18
因为 let 可以修改,所以类型推断通常会更宽泛。
3. const 声明对象,对象内部可以修改
这一点非常重要。
const user = {
name: "张三",
age: 18
};
// ✅ 可以修改对象属性
user.name = "李四";
user.age = 20;
// ❌ 不能重新赋值整个对象
user = {
name: "王五",
age: 30
};
为什么?
因为 const 限制的是变量绑定不能重新赋值,而不是对象内部的数据不能变化。
可以理解为:
const user ──────> 对象 A
name: 张三
age: 18
你可以修改对象 A 的内容,但是不能让 user 指向另一个对象。
4. const 声明数组,数组仍然可以修改
const numbers = [1, 2, 3];
// ✅ 可以修改
numbers.push(4);
numbers[0] = 100;
console.log(numbers);
// [100, 2, 3, 4]
// ❌ 不能重新赋值
numbers = [5, 6, 7];
同样道理,const 只是不允许变量重新赋值。
5. as const:让对象属性不可修改
这是 TypeScript 特别常用的语法。
const user = {
name: "张三",
age: 18
} as const;
此时 TypeScript 推断出来的类型是:
{
readonly name: "张三";
readonly age: 18;
}
如果修改:
user.name = "李四"; // ❌ 报错
user.age = 20; // ❌ 报错
注意,这些是 TypeScript 编译阶段的限制,不会自动在运行时冻结对象。
const 和 as const 对比
// 普通 const
const user1 = {
name: "张三",
age: 18
};
user1.name = "李四"; // ✅
// as const
const user2 = {
name: "张三",
age: 18
} as const;
user2.name = "李四"; // ❌
6. as const 用于数组
普通数组:
const arr = [1, 2, 3];
arr.push(4); // ✅
arr[0] = 100; // ✅
使用 as const:
const arr = [1, 2, 3] as const;
arr.push(4); // ❌
arr[0] = 100; // ❌
此时数组类型变成:
readonly [1, 2, 3]
也就是说,as const 将其推断为只读元组。
7. const 和 readonly 区别
readonly 主要用于对象属性和类成员。
interface User {
readonly id: number;
name: string;
}
const user: User = {
id: 1001,
name: "张三"
};
user.name = "李四"; // ✅
user.id = 1002; // ❌
区别在于:
| 特性 | const | readonly | as const |
|---|---|---|---|
| 限制变量重新赋值 | ✅ | — | — |
| 限制对象属性修改 | ❌ | ✅ | ✅ |
| 保留字面量类型 | 简单常量通常会 | 不一定 | ✅ |
| 适用于 | 变量声明 | 对象属性、类属性 | 表达式 |
| 运行时冻结对象 | ❌ | ❌ | ❌ |
8. 实际业务场景:定义状态常量
假设有一个任务调度系统,包含三种状态:
const TASK_STATUS = {
PENDING: "pending",
RUNNING: "running",
SUCCESS: "success",
FAILED: "failed"
} as const;
现在想从它自动生成一个联合类型:
type TaskStatus =
typeof TASK_STATUS[keyof typeof TASK_STATUS];
此时 TaskStatus 等价于:
type TaskStatus =
| "pending"
| "running"
| "success"
| "failed";
就可以这样使用:
interface Task {
taskId: string;
status: TaskStatus;
}
const task: Task = {
taskId: "123",
status: TASK_STATUS.RUNNING
};
如果传入不合法的状态:
const task: Task = {
taskId: "123",
status: "unknown" // ❌ 编译错误
};
这是 as const 在 TypeScript 项目里非常典型的应用,尤其适合定义任务状态、接口枚举值、业务常量。
9. TypeScript 还有 const 泛型
TypeScript 5.0 开始支持 const 类型参数。
普通泛型:
function createConfig<T>(config: T): T {
return config;
}
const config = createConfig({
status: "success",
retry: 3
});
这里 status 通常会推断为 string。
使用 const 泛型:
function createConfig<const T>(config: T): T {
return config;
}
const config = createConfig({
status: "success",
retry: 3
});
此时推断类型更精确:
{
readonly status: "success";
readonly retry: 3;
}
它的作用是让泛型参数优先采用类似 as const 的字面量推断方式。
总结
记住下面四个例子就够了:
// 1. const:变量不能重新赋值
const a = 10;
// 2. const 对象:内部属性可以修改
const b = { name: "张三" };
b.name = "李四"; // ✅
// 3. as const:属性只读,字面量类型更精确
const c = { name: "张三" } as const;
// c.name = "李四"; // ❌
// 4. readonly:指定属性不可修改
interface User {
readonly id: number;
name: string;
}
一句话理解:
const 管的是变量能不能重新赋值,readonly 管的是属性能不能修改,而 as const 是让整个表达式得到更精确的只读类型(对对象和数组的字面量结构递归生效)。