如何实现多值映射同一键的TypeScript类枚举类型
如何用单个TypeScript类型实现多字符串值映射同一枚举键?
问题背景
我正尝试将代码库中原本使用字符串类型的部分转换为TypeScript的类枚举类型,但部分场景下字符串值存在细微差异(比如"PICKED UP"和"PICKED_UP")。不想创建两个几乎完全相同的枚举类型,希望用单个类型让多个值匹配同一个键。
我的尝试代码(无法正常工作):
const STATUS = { CREATED: "CREATED", PICKED_UP: "PICKED UP" | "PICKED_UP", DELIVERED: "DELIVERED" | "DEL" } as const type ObjectValues<T> = T[keyof T]; type Status = ObjectValues<typeof STATUS>;
期望效果:
// 原期望逻辑存在矛盾,需调整为判断值是否属于对应键的有效值 // console.log(STATUS.PICKED_UP === "PICKED UP" && STATUS.PICKED_UP === "PICKED_UP") // 无法实现,单一变量不能同时等于两个不同字符串 doThingWithStatus("PICKED_UP") // 应通过类型检查 doThingWithStatus("PICKED UP") // 也应通过类型检查
问题核心
你原来的代码混淆了TypeScript编译时类型和JavaScript运行时值的边界:联合类型是编译时的类型约束,运行时对象的属性只能存储单一值,不可能让STATUS.PICKED_UP同时等于两个不同的字符串。
解决方案
通过「类型层面兼容多值」+「运行时做值的匹配/标准化」实现需求,分三步完成:
1. 类型定义(满足类型约束)
先定义所有合法的状态字符串值,再建立键与对应有效值的类型映射:
// 所有允许的状态字符串值(覆盖所有差异格式) type Status = "CREATED" | "PICKED UP" | "PICKED_UP" | "DELIVERED" | "DEL"; // 键到对应有效值的类型映射(仅用于类型推导) type StatusKeyMap = { CREATED: "CREATED"; PICKED_UP: "PICKED UP" | "PICKED_UP"; DELIVERED: "DELIVERED" | "DEL"; };
2. 运行时工具(处理值的匹配与标准化)
创建运行时的映射表和工具函数,用来判断值是否属于某个键的有效值,或把不同格式的值统一为标准键:
// 运行时的有效值映射表 const STATUS_VALUES = { CREATED: ["CREATED"], PICKED_UP: ["PICKED UP", "PICKED_UP"], DELIVERED: ["DELIVERED", "DEL"], } as const; // 判断字符串是否属于指定键的有效值 function isStatusValueForKey(value: string, key: keyof typeof STATUS_VALUES): boolean { return STATUS_VALUES[key].includes(value as any); } // 将任意合法Status值转换为统一的标准键(比如用下划线版本) function normalizeStatus(value: Status): keyof StatusKeyMap { switch(value) { case "PICKED UP": return "PICKED_UP"; case "DEL": return "DELIVERED"; default: return value as keyof StatusKeyMap; } }
3. 实际使用
- 类型约束:函数参数直接用
Status类型,自动接受所有合法值 - 运行时处理:先标准化值,再按标准键处理业务逻辑
function doThingWithStatus(status: Status) { const normalizedKey = normalizeStatus(status); switch(normalizedKey) { case "CREATED": console.log("处理创建状态"); break; case "PICKED_UP": console.log("处理取货状态(兼容空格/下划线格式)"); break; case "DELIVERED": console.log("处理配送状态(兼容完整/简写格式)"); break; } } // 所有合法调用均通过类型检查 doThingWithStatus("PICKED_UP"); // ok doThingWithStatus("PICKED UP"); // ok doThingWithStatus("DEL"); // ok doThingWithStatus("CREATED"); // ok // 替代原期望的判断逻辑 console.log(isStatusValueForKey("PICKED UP", "PICKED_UP")); // true console.log(isStatusValueForKey("PICKED_UP", "PICKED_UP")); // true
简化方案(贴近枚举使用)
如果想更贴近原生枚举的使用方式,可以创建标准枚举对象,同时让类型兼容所有等效值:
// 标准状态枚举对象(运行时用统一值) const STATUS = { CREATED: "CREATED" as const, PICKED_UP: "PICKED_UP" as const, DELIVERED: "DELIVERED" as const, }; // 类型层面兼容所有差异格式的值 type Status = (typeof STATUS)[keyof typeof STATUS] | "PICKED UP" | "DEL";
这样STATUS.PICKED_UP是标准的"PICKED_UP",而类型Status同时接受差异格式的值,满足类型约束需求。
内容的提问来源于stack exchange,提问作者Squiggle
相关产品推荐
相关产品推荐

