资讯动态

TypeScript中,字符串字面量联合类型(Union Type)、enum的用法说明

发布时间:2026/9/1 23:51:52 来源:尧图企业网站定制
1、使用字符串字面量联合类型Union Type优点零运行时开销缺点不支持重命名符号重构不方便// 1. 使用字符串字面量联合类型Union Type优点零运行时开销缺点不支持重命名符号重构不方便 /** * 操作指令新增删除info-add修改info-modify查看info-view */ export type OperationCommand info-add | info-modify| info-view;2、使用枚举 enum优点有利于重构IDE 重命名符号缺点增加性能开销// 2. 使用枚举 enum优点有利于重构IDE 重命名符号缺点增加性能开销 /** * 操作指令 */ export enum OperationCommand { /** * 新增删除 */ ADD info-add, /** * 修改 */ MODIFY info-modify, /** * 查看 */ VIEW info-view }3、使用 as const 常量对象 类型别名零运行时开销有利于重构IDE 重命名符号// 3. 使用 as const 常量对象 类型别名零运行时开销有利于重构IDE 重命名符号 /** * 操作指令新增删除info-add修改info-modify查看info-view */ export const OperationCommand { /** * 新增删除 */ ADD: info-add, /** * 修改 */ MODIFY: info-modify, /** * 查看 */ VIEW: info-view } as const; // 从常量对象推导出联合类型 export type OperationCommand (typeof OperationCommand)[keyof typeof OperationCommand];在 TypeScript 中对于表示一组固定字符串值的场景通常推荐使用字符串字面量联合类型Union Type而不是enum。您当前的写法tsexport type OperationCommand info-view | info-modify | info-add;已经是比较现代且推荐的做法。为什么字符串联合类型更好1. 零运行时开销enum会编译成一个真实的 JavaScript 对象除非使用const enum增加代码体积。而字符串联合类型仅在编译时存在编译后会被完全擦除不会产生任何运行时代码。ts// enum 编译后 var OperationCommand; (function (OperationCommand) { OperationCommand[info-view] info-view; // ... })(OperationCommand || (OperationCommand {})); // 字符串联合类型编译后完全消失2. 与 JavaScript 互操作性更好字符串联合类型本质上就是普通字符串可以直接与后端传来的字符串、环境变量、JSON 数据等交互无需类型转换。而enum的值在某些场景下可能需要额外处理如数字枚举。字符串枚举虽然值是字符串但类型名不同跨模块使用时可能需要导入。3. 简单直观易于维护联合类型一目了然不需要关心enum的命名空间、反向映射等特性。添加或删除一个值只需修改类型定义所有使用处会立即获得类型检查。4. 现代 TypeScript 官方推荐TypeScript 官方文档在“String Literal Types vs Enums”部分明确表示使用字符串字面量联合类型通常是更好的选择除非你有非常充足的理由使用enum例如需要迭代所有成员、希望有命名空间等。何时可以考虑使用enum需要遍历所有枚举值如生成下拉选项。希望在运行时能够通过名字访问值如OperationCommand[info-add]。团队已经大量使用enum且希望保持一致。但即便需要遍历也可以使用as const对象配合类型推导来模拟enum同时保持零运行时开销和简洁性tsexport const OperationCommand { VIEW: info-view, MODIFY: info-modify, ADD: info-add, } as const; export type OperationCommand (typeof OperationCommand)[keyof typeof OperationCommand];这样既拥有类似enum的命名空间和遍历能力又避免了enum的额外运行时对象和潜在问题。结论您的现有代码字符串联合类型是正确且推荐的做法无需改为enum。如果项目中没有特殊需求继续使用字符串联合类型即可。只有在确实需要枚举的运行时特性时才考虑enum或者使用as const对象作为折中。您的观察很准确enum在 VS Code 中确实提供了更好的“重命名符号”体验因为枚举成员是明确的符号使用F2重命名时会自动更新所有引用。而字符串联合类型的字面量分散在代码各处重命名类型别名只能改类型定义不会修改字符串值本身需要手动查找替换。如何兼顾as const对象 类型别名采用as const常量对象来定义命令值再通过类型推导生成联合类型。这样既保留了enum的符号化引用可重命名、可查找引用又避免了enum的运行时开销和潜在问题。示例typescript// 定义常量对象键名用大写标识符值保持不变 export const OperationCommand { VIEW: info-view, MODIFY: info-modify, ADD: info-add, } as const; // 从常量对象推导出联合类型 export type OperationCommand (typeof OperationCommand)[keyof typeof OperationCommand];使用方式typescript// 符号化引用VS Code 可识别并支持重命名 const cmd: OperationCommand OperationCommand.ADD; // 或者直接使用字面量类型依然安全 const cmd2: OperationCommand info-add;优点重命名方便选中OperationCommand.ADD按F2可同时重命名所有使用该常量的地方。保留类型安全联合类型仍然有效可直接使用字符串字面量。零额外运行时开销as const对象编译后就是一个普通对象也可以被 tree-shaking 消除如果未使用不像enum会生成额外代码。可遍历如果需要生成下拉选项等可以直接Object.values(OperationCommand)。与 JavaScript 互操作友好值就是普通字符串没有enum的包装。总结如果看重重构便利性且团队习惯使用enum可以继续使用enum因为它的成员是真正的符号VS Code 支持完善。如果希望避免enum的缺点运行时开销、与 JS 互操作性等又想拥有类似的符号化能力强烈推荐as const对象 类型别名模式。您可以根据项目实际情况和团队偏好选择。两种方式都能在 VS Code 中实现良好的重构体验。使用示例使用 as const 常量对象 类型别名示例1import { CommonOperationCommand } from /types; provide(operationCommandSymbol, refCommonOperationCommand(info-view));示例2import { CommonOperationCommand } from /types; provide(operationCommandSymbol, refCommonOperationCommand(CommonOperationCommand.VIEW));是的这样使用完全正确而且比直接使用字符串字面量更好。typescriptimport { CommonOperationCommand } from /types; provide(operationCommandSymbol, refCommonOperationCommand(CommonOperationCommand.VIEW));为什么推荐这种用法符号化引用使用CommonOperationCommand.VIEW而不是字符串字面量info-view让 VS Code 能够识别为符号支持F2 重命名和查找所有引用便于后续重构。类型安全如果将来修改了CommonOperationCommand.VIEW的值例如改为view所有使用该常量的地方会自动反映而字符串字面量需要手动查找替换容易遗漏。避免拼写错误使用常量时TypeScript 会在编译期检查成员名是否正确而直接写字符串字面量时如果拼写错误如info-vuew虽然类型检查会报错因为不是联合类型成员但不如常量成员名直观。与前一种用法的对比前一种用法tsprovide(operationCommandSymbol, refCommonOperationCommand(info-view));也是合法的因为info-view是联合类型的成员。但它的值是以裸字符串形式存在的重构时不受保护。需要注意的地方确保CommonOperationCommand同时导出了值和类型当前定义已经满足。如果tsconfig.json开启了verbatimModuleSyntax或isolatedModules可能需要将类型导入分离tsimport { CommonOperationCommand } from /types; // 导入值 import type { CommonOperationCommand as CommonOperationCommandType } from /types; // 导入类型 // 使用时类型写作refCommonOperationCommandType但多数项目默认配置不会要求这样可以直接混用。总之您给出的用法是标准且推荐的做法。import { CommonOperationCommand } from /types; 这里不是仅仅导入了值吗 那为什么这里 provide(operationCommandSymbol, refCommonOperationCommand(CommonOperationCommand.VIEW)); 可以当作类型呢refCommonOperationCommand这是 TypeScript 中很常见的同名导出value type用法理解起来需要区分类型空间和值空间。1. 同名导出你的模块中同时导出了一个值export const CommonOperationCommand { ... } as const;一个类型export type CommonOperationCommand ...这两个导出名称相同但位于不同的“空间”值空间变量、常量、函数、类实例等。类型空间类型别名、接口、类类型等。因为空间不同所以可以共存不会冲突。2. 导入时的行为import { CommonOperationCommand } from /types;会同时引入值和类型两个含义因为同一个名字在两个空间中都有声明。TypeScript 会根据使用位置自动决定取哪个在值位置如CommonOperationCommand.VIEW使用就指值常量对象。在类型位置如refCommonOperationCommand的尖括号内使用就指类型联合类型。所以你的代码完全合法typescriptprovide(operationCommandSymbol, refCommonOperationCommand(CommonOperationCommand.VIEW)); // ↑ 类型位置联合类型 ↑ 值位置常量对象3. 为什么不用分开导入默认 TypeScript 配置下这种混合导入是允许的TS 编译器能正确理解。但如果你开启了某些严格选项如isolatedModules或verbatimModuleSyntaxTS 可能会提示需要分开导入因为它需要明确区分类型导入和值导入以支持单文件转译工具如 esbuild、swc准确工作。此时可以这样写typescriptimport { CommonOperationCommand } from /types; // 值导入 import type { CommonOperationCommand as CommonOperationCommandType } from /types; // 类型导入 // 使用类型 provide(operationCommandSymbol, refCommonOperationCommandType(CommonOperationCommand.VIEW));但如果你没有启用这些选项你的写法就是最简洁的无需改动。总结import { CommonOperationCommand }同时导入了值和类型。TS 根据语法位置自动识别你的用法完全正确。只需注意项目 tsconfig 是否启用了verbatimModuleSyntax等严格选项如果有则需要分离导入。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价