如何通过接口在反序列化时将错误字符串映射为指定枚举值?
解决方案
1. 把修正逻辑封装到枚举命名空间
不改动原有枚举定义,通过命名空间扩展枚举的转换能力,将错误值映射逻辑集中在枚举相关代码里,避免影响现有接口和泛型函数:
enum Bar { bar = "bar", qux = "qux", } namespace Bar { // 定义错误值到正确枚举值的映射表 const errorValueMap: Record<string, Bar> = { "baz": Bar.bar }; // 单个值转换函数 export function fromRawValue(value: string): Bar { // 优先匹配正常枚举值,匹配失败则查错误映射,最后可根据业务设置默认值或抛出错误 return Bar[value as keyof typeof Bar] ?? errorValueMap[value] ?? Bar.bar; } // 数组批量转换函数 export function fromRawArray(values: string[]): Bar[] { return values.map(fromRawValue); } } // 原有接口保持不变 interface Data1 { foo: Bar[]; } interface Data2 { foo: Bar[]; } // 泛型fetch函数无需修改 async function fetchData<T>() { const response = await fetch("data.json"); return await response.json() as T; } // 使用时仅需在获取数据后调用转换函数处理对应字段 async function getProcessedData1(): Promise<Data1> { const rawData = await fetchData<{ foo: string[] }>(); return { foo: Bar.fromRawArray(rawData.foo) }; } const data: Data1 = await getProcessedData1();
2. 结合类型守卫实现类型安全的转换
如果需要更强的类型校验,可通过类型守卫确保转换后的值严格符合枚举类型:
enum Bar { bar = "bar", qux = "qux", } // 类型守卫:判断值是否为合法Bar枚举值 function isValidBar(value: string): value is Bar { return Object.values(Bar).includes(value as Bar); } // 转换函数:处理合法值与错误值 function convertToBar(value: string): Bar { if (isValidBar(value)) { return value; } switch (value) { case "baz": return Bar.bar; default: // 可根据业务需求选择抛出错误或返回默认值 throw new Error(`无效的Bar值:${value}`); } } // 使用方式类似,转换对应字段即可 async function getProcessedData2(): Promise<Data2> { const rawData = await fetchData<{ foo: string[] }>(); return { foo: rawData.foo.map(convertToBar) }; }
3. 自定义JSON解析器(零侵入调用)
如果希望完全保留原有fetch调用方式,可替换默认的JSON解析逻辑,在解析阶段自动修正错误值:
enum Bar { bar = "bar", qux = "qux", } // 自定义JSON解析函数,递归遍历并修正Bar类型的错误值 function parseWithBarFix<T>(jsonStr: string): T { const parsed = JSON.parse(jsonStr); function processValue(value: any): any { if (Array.isArray(value)) { return value.map(processValue); } if (typeof value === "string") { // 处理错误值映射 if (value === "baz") return Bar.bar; // 保留合法枚举值 if (Object.values(Bar).includes(value)) return value; } if (typeof value === "object" && value !== null) { for (const key in value) { value[key] = processValue(value[key]); } } return value; } return processValue(parsed) as T; } // 仅修改fetchData内的解析逻辑,调用方式完全不变 async function fetchData<T>() { const response = await fetch("data.json"); const jsonStr = await response.text(); return parseWithBarFix<T>(jsonStr); } // 原有调用代码无需任何改动 const data: Data1 = await fetchData<Data1>();
方案对比
- 方案1:逻辑集中,对现有代码改动最小,适合需要明确控制转换流程的场景。
- 方案2:类型安全性更高,能提前拦截非法值,适合对类型严谨性要求高的业务。
- 方案3:调用方式零侵入,但需注意递归遍历的边界情况(如嵌套对象、其他类型字段的兼容)。
内容的提问来源于stack exchange,提问作者Cappucirno
相关产品推荐
相关产品推荐

