typescript 中const用法

TypeScript 中的 const 主要用于声明一个不能被重新赋值的变量。

不过,TypeScript 中有三个容易混淆的概念:

  • const:声明常量,变量不能重新赋值。
  • as const:常量断言,让类型更加精确,并添加只读约束。
  • readonly:限制对象属性不能被修改。

下面通过例子说明。

1. const 基础用法

ini 复制代码
const name = "张三";
const age = 18;
const isAdmin = true;

console.log(name); // 张三

name = "李四"; // ❌ 报错

因为 const 声明的变量不能重新赋值。

对比 let:

ini 复制代码
let name = "张三";

name = "李四"; // ✅ 可以修改

2. const 也可以指定类型

ini 复制代码
const age: number = 18;

const name: string = "张三";

const isAdmin: boolean = true;

但 TypeScript 有类型推断,所以通常不需要显式声明。

ini 复制代码
const age = 18;

TypeScript 会自动推断 age 的类型是字面量类型 18,而不是普通的 number。

对比:

ini 复制代码
let age = 18;   // number
const age2 = 18; // 18

因为 let 可以修改,所以类型推断通常会更宽泛。

3. const 声明对象,对象内部可以修改

这一点非常重要。

ini 复制代码
const user = {
  name: "张三",
  age: 18
};

// ✅ 可以修改对象属性
user.name = "李四";
user.age = 20;

// ❌ 不能重新赋值整个对象
user = {
  name: "王五",
  age: 30
};

为什么?

因为 const 限制的是变量绑定不能重新赋值,而不是对象内部的数据不能变化。

可以理解为:

yaml 复制代码
const user ──────> 对象 A
                    name: 张三
                    age: 18

你可以修改对象 A 的内容,但是不能让 user 指向另一个对象。

4. const 声明数组,数组仍然可以修改

ini 复制代码
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 特别常用的语法。

csharp 复制代码
const user = {
  name: "张三",
  age: 18
} as const;

此时 TypeScript 推断出来的类型是:

bash 复制代码
{
  readonly name: "张三";
  readonly age: 18;
}

如果修改:

ini 复制代码
user.name = "李四"; // ❌ 报错
user.age = 20;     // ❌ 报错

注意,这些是 TypeScript 编译阶段的限制,不会自动在运行时冻结对象。

const 和 as const 对比

ini 复制代码
// 普通 const
const user1 = {
  name: "张三",
  age: 18
};

user1.name = "李四"; // ✅


// as const
const user2 = {
  name: "张三",
  age: 18
} as const;

user2.name = "李四"; // ❌

6. as const 用于数组

普通数组:

ini 复制代码
const arr = [1, 2, 3];

arr.push(4);  // ✅
arr[0] = 100; // ✅

使用 as const:

ini 复制代码
const arr = [1, 2, 3] as const;

arr.push(4);  // ❌
arr[0] = 100; // ❌

此时数组类型变成:

bash 复制代码
readonly [1, 2, 3]

也就是说,as const 将其推断为只读元组。

7. const 和 readonly 区别

readonly 主要用于对象属性和类成员。

ini 复制代码
interface User {
  readonly id: number;
  name: string;
}

const user: User = {
  id: 1001,
  name: "张三"
};

user.name = "李四"; // ✅

user.id = 1002; // ❌

区别在于:

特性 const readonly as const
限制变量重新赋值 ✅ --- ---
限制对象属性修改 ❌ ✅ ✅
保留字面量类型 简单常量通常会 不一定 ✅
适用于 变量声明 对象属性、类属性 表达式
运行时冻结对象 ❌ ❌ ❌

8. 实际业务场景:定义状态常量

假设有一个任务调度系统,包含三种状态:

csharp 复制代码
const TASK_STATUS = {
  PENDING: "pending",
  RUNNING: "running",
  SUCCESS: "success",
  FAILED: "failed"
} as const;

现在想从它自动生成一个联合类型:

ini 复制代码
type TaskStatus =
  typeof TASK_STATUS[keyof typeof TASK_STATUS];

此时 TaskStatus 等价于:

bash 复制代码
type TaskStatus =
  | "pending"
  | "running"
  | "success"
  | "failed";

就可以这样使用:

css 复制代码
interface Task {
  taskId: string;
  status: TaskStatus;
}

const task: Task = {
  taskId: "123",
  status: TASK_STATUS.RUNNING
};

如果传入不合法的状态:

arduino 复制代码
const task: Task = {
  taskId: "123",
  status: "unknown" // ❌ 编译错误
};

这是 as const 在 TypeScript 项目里非常典型的应用,尤其适合定义任务状态、接口枚举值、业务常量。

9. TypeScript 还有 const 泛型

TypeScript 5.0 开始支持 const 类型参数。

普通泛型:

lua 复制代码
function createConfig<T>(config: T): T {
  return config;
}

const config = createConfig({
  status: "success",
  retry: 3
});

这里 status 通常会推断为 string。

使用 const 泛型:

arduino 复制代码
function createConfig<const T>(config: T): T {
  return config;
}

const config = createConfig({
  status: "success",
  retry: 3
});

此时推断类型更精确:

bash 复制代码
{
  readonly status: "success";
  readonly retry: 3;
}

它的作用是让泛型参数优先采用类似 as const 的字面量推断方式。

总结

记住下面四个例子就够了:

ini 复制代码
// 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 是让整个表达式得到更精确的只读类型(对对象和数组的字面量结构递归生效)。

相关推荐
程序员大阳11 小时前
HTML+CSS+JS对话式实战(3)-活动管理页实现查询、新增、删除API后端接口
javascript·css·html·实战·删除·查询·新增
炫酷的大高个11 小时前
6 亿播放的闪身步,我用前端算出 15 倍重心差
前端·javascript
林小帅12 小时前
【笔记】Prisma v7 + SQLite TS2305 及连锁 TS7006 问题
前端·javascript·后端
Dumbness12 小时前
订阅消息弹窗必须在用户点击里调:联调实验室里 timing 和 Mock 支付通知踩的坑
javascript·uni-app
黄连木笛12 小时前
不改一行 Postman 源码,把它汉化了:Electron preload 注入 + fetch 拦截实战
javascript·electron
叼烟扛炮14 小时前
[LangChain] 学习笔记(03):打破失忆症——对话机制与上下文管理
java·前端·javascript·笔记·学习·langchain
卋罖75015 小时前
Razor 视图 + JavaScript + Vue/iView 组件实现的表格自定义列
javascript·vue·iview·razor视图
系统集成-鹰翔1 天前
ERP图片改名后仍命中识别缓存:缓存键、十分钟到期与调用计数
javascript·经验分享·后端
FlyWIHTSKY1 天前
vue2和vue3有什么区别,用法上有哪些区别
前端·javascript·vue.js