一些特性
约 1604 字大约 5 分钟
2025-11-12
as const
默认的可变性问题
在 TypeScript 中,数组和对象默认是可变的,这意味着:
- 类型推断过于宽泛:TypeScript 会为可变集合推断出最通用的类型
- 类型安全性降低:无法有效捕获意外的类型错误
- 自动补全不准确:IDE 无法提供精确的成员建议
const colors = ["red", "green", "blue"];
// Type: string [] - 类型太宽泛,可能是任何字符串数组
colors.push("yellow"); // 允许操作,但可能不是您想要的行为
// 推断类型: Color = string (过于宽泛!)问题详解
类型推断结果
colors被推断为string[]类型- 这个类型包含了所有可能的字符串数组组合
潜在问题
// 允许添加任意字符串,这可能违背设计意图
colors.push("purple"); // 允许
colors.push(123); // 错误:Type 'number' is not assignable to type 'string'
// 类型提取结果过于宽泛
type Color = (typeof colors)[number]; // string,而不是具体的颜色值解决方案
使用 as const 断言
const colors = ["red", "green", "blue"] as const;
// Type: readonly ["red", "green", "blue"]
// 现在类型提取是精确的
type Color = (typeof colors)[number]; // "red" | "green" | "blue" | "yellow"
// 仍然允许添加,但类型检查更严格
colors.push("yellow"); // 允许,但会更新类型
// 以下操作会被 TypeScript 阻止
colors[0] = "pink"; // Type 'string' is not assignable to type '"red"'
colors.push("orange"); // 如果不在原始类型中,会报错使用明确的类型定义
type Color = "red" | "green" | "blue" | "yellow";
const colors: Color[] = ["red", "green", "blue"];
// Type: Color [] - 明确指定允许的类型联合类型方法
// 定义允许的具体值
const ALLOWED_COLORS = ["red", "green", "blue"] as const;
type AllowedColor = (typeof ALLOWED_COLORS)[number];
// 创建可变数组,但限制为允许的类型
const colors: AllowedColor[] = [...ALLOWED_COLORS];
// 使用
colors.push("yellow"); // 错误:Argument of type '"yellow"' is not assignable
colors.push("purple"); // 错误:Argument of type '"purple"' is not assignable实际应用场景
配置对象
// 不推荐:类型太宽泛
const config = {
environment: "development",
debug: true,
port: 3000
};
// 推荐:使用 const 断言
const config = {
environment: "development",
debug: true,
port: 3000
} as const;
type ConfigKey = (typeof config)[keyof typeof config];
// "development" | true | 3000相关信息
keyof 是 类型操作符,它的作用是:获取一个对象类型中所有键(key)的 联合类型。
keyof typeof config
// => "environment" | "debug" | "port"加了 as const 就类似把数组变成了元组,更精确地说,是 只读字面量元组(readonly tuple)。
常见适用场景
场景一:固定配置、枚举、选项常量
当你需要定义一组 固定的不可变配置,让 TS 精确知道有哪些值时:
const COLORS = ["red", "green", "blue"] as const;
type Color = typeof COLORS[number];
// => "red" | "green" | "blue"典型应用:
- 定义枚举选项
- 受控组件的选项列表
- 状态机、API 常量等
场景二:对象键值映射
当你想让对象的值类型也被“锁死”成具体值时:
const STATUS = {
SUCCESS: 200,
NOT_FOUND: 404,
SERVER_ERROR: 500
} as const;
type StatusCode = (typeof STATUS)[keyof typeof STATUS];
// => 200 | 404 | 500若不加 as const,推断类型会变为 { SUCCESS: number; ... },类型丢失。
场景三:switch / 映射判断 更精确的类型推断
比如写一个类型安全的 switch:
const actions = ["start", "stop", "pause"] as const;
type Action = typeof actions[number]; // "start" | "stop" | "pause"
function handle(action: Action) {
switch (action) {
case "start": ...
case "stop": ...
case "pause": ...
}
}如果不加 as const,action 就会被当作普通 string,类型保护失效。
场景四:Redux、状态管理、命令模式等
当你写 action creator 时:
const actions = {
ADD_TODO: "ADD_TODO",
REMOVE_TODO: "REMOVE_TODO"
} as const;
type ActionType = keyof typeof actions;
// => "ADD_TODO" | "REMOVE_TODO"
type ActionValue = typeof actions[ActionType];
// => "ADD_TODO" | "REMOVE_TODO"as const 可以防止字符串被“宽化”,让类型和值一一对应。
场景五:模板映射、API 参数定义
定义接口字段、HTTP 请求方法等:
const METHODS = ["GET", "POST", "PUT", "DELETE"] as const;
type Method = typeof METHODS[number]; // "GET" | "POST" | "PUT" | "DELETE"这种写法广泛用于 SDK、API 客户端、配置校验中。
使用 as const 的权衡
| 优点 | 缺点 |
|---|---|
| 提高类型精度(从宽泛 → 精确) | 所有字段变 readonly(不能改) |
| 适合定义常量、枚举、配置 | 不能再修改值(需复制) |
让 keyof / [number] 提取类型更安全 | 不适合需要动态构建的结构 |
as const 的本质是告诉 TypeScript:“这个值就是常量,不会再改动,请用最精确的类型去推断它。”
常用搭配套路
// 1️⃣ 枚举值提取
const ROLES = ["admin", "user", "guest"] as const;
type Role = typeof ROLES[number];
// 2️⃣ 对象 value 提取
const HTTP = { GET: "GET", POST: "POST" } as const;
type HttpMethod = (typeof HTTP)[keyof typeof HTTP];
// 3️⃣ 状态机
const STATES = { INIT: 0, RUNNING: 1, DONE: 2 } as const;
type State = (typeof STATES)[keyof typeof STATES];不过很多 as const + 类型提取 的场景,从逻辑上来看,用 TypeScript 的枚举(enum)也能实现同样的效果,甚至在某些场合还更直观:
enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
let c: Color = Color.Green; // 类型安全as const 的优势:
更轻量
as const只是类型层面的“常量标记”,不会生成任何 JS 运行时代码。enum默认会生成运行时代码(尤其是字符串枚举),会增加打包体积。
更灵活
- 可以直接对数组或对象使用,不需要先定义枚举,再维护两个映射。
- 特别适合动态生成的常量数组,只要加上
as const就能推导类型。
const COLORS = getColorsFromAPI() as const; // 类型会锁死字面量更好推导联合类型
- 用
as const+typeof+[number]很容易得到联合类型:
const COLORS = ["red", "green", "blue"] as const;
type Color = typeof COLORS[number]; // "red" | "green" | "blue"枚举想得到类似 "red" | "green" | "blue" 就稍麻烦,通常要用 keyof typeof Enum 或者 Enum[keyof typeof Enum]。
与对象结构配合更自然
- 当你要做对象字面量映射时,
as const更方便:
const STATUS = { SUCCESS: 200, ERROR: 500 } as const;
type StatusCode = typeof STATUS[keyof typeof STATUS]; // 200 | 500枚举也能做,但对象写法更直观,尤其是值不连续或非数字的场景。
| 方式 | 优势 | 适合场景 |
|---|---|---|
as const | 类型层面精确、轻量、灵活 | 常量数组、对象、状态码、联合类型提取 |
enum | JS 运行时代码、可直接使用、直观 | 明确枚举、状态机、需要运行时代码引用的常量 |
as const 更像是“类型安全的轻量写法”,枚举更像是“正式的运行时代码枚举”,两者重叠的场景确实很多,但 as const 更灵活、轻量,特别适合做联合类型提取。