如何从API错误对象生成TypeScript的RestError接口实例
如何优雅生成符合TypeScript接口的RestError实例(处理非合法JSON的API错误返回)
API返回的错误数据格式类似JS对象字面量,而非合法JSON:
error: { message: "entry doesn't exist", code: '4', target: 'uuid' }
需要将其转换为符合以下TypeScript接口的类型化实例:
export interface RestError { error: { target: string; message: string; code: string; arguments?: { message: string; code: string; }[]; }; }
直接使用JSON.parse会抛出SyntaxError,目前采用手动工具函数填充,以下是几种更优雅规范的实现方式:
方案1:使用JSON5解析非严格JSON格式
JSON5是专门支持JS风格对象字面量的解析库,允许无引号的键、单引号字符串等非严格JSON语法,无需自己写复杂正则修复格式。
步骤:
- 安装JSON5:
npm install json5 # 或 yarn add json5
- 解析并转换为RestError实例:
import JSON5 from 'json5'; import type { RestError } from './your-interface-path'; function parseRestError(rawErrorText: string): RestError { // 给原始内容包裹外层对象(API返回的是`error: {...}`而非完整对象) const wrappedText = `{${rawErrorText.trim()}}`; // 使用JSON5解析非严格格式 const parsed = JSON5.parse(wrappedText); // 类型转换与适配接口 return { error: { target: parsed.error.target, message: parsed.error.message, code: String(parsed.error.code), // 强制转为字符串匹配接口类型 arguments: parsed.error.arguments?.map((arg: any) => ({ message: arg.message, code: arg.code })) ?? undefined } }; }
这个方案简洁安全,能处理绝大多数非严格JSON场景,是最推荐的实现方式。
方案2:类型安全的手动转换函数(无第三方依赖)
如果无法引入第三方库,可编写兼具格式修复、解析和类型校验的工具函数,确保生成的实例严格符合RestError接口:
import type { RestError } from './your-interface-path'; function toRestError(rawErrorText: string): RestError { // 步骤1:修复格式为合法JSON let fixedText = rawErrorText.trim(); // 包裹外层对象 if (!fixedText.startsWith('{')) { fixedText = `{${fixedText}}`; } // 给无引号的键添加双引号 fixedText = fixedText.replace(/(\w+):/g, '"$1":'); // 将单引号替换为双引号,并转义字符串内的单引号 fixedText = fixedText.replace(/'/g, '\\"'); // 步骤2:解析JSON let parsed: unknown; try { parsed = JSON.parse(fixedText); } catch (err) { throw new Error(`解析错误数据失败: ${(err as Error).message}`); } // 步骤3:类型校验与转换 if (typeof parsed !== 'object' || parsed === null || !('error' in parsed)) { throw new Error('错误数据结构不符合预期'); } const errorData = parsed.error as unknown; if (typeof errorData !== 'object' || errorData === null) { throw new Error('error字段必须为对象'); } const { target, message, code, arguments: args } = errorData as { target?: unknown; message?: unknown; code?: unknown; arguments?: unknown; }; // 校验必填字段类型 if (typeof target !== 'string' || typeof message !== 'string') { throw new Error('target或message字段缺失或类型错误'); } if (typeof code !== 'string' && typeof code !== 'number') { throw new Error('code字段必须为字符串或数字'); } // 处理可选的arguments数组 let processedArgs: RestError['error']['arguments'] | undefined; if (Array.isArray(args)) { processedArgs = args.filter(arg => typeof arg === 'object' && arg !== null && typeof (arg as any).message === 'string' && typeof (arg as any).code === 'string' ).map(arg => ({ message: (arg as any).message, code: (arg as any).code })); // 空数组转为undefined(适配可选字段) processedArgs = processedArgs.length > 0 ? processedArgs : undefined; } return { error: { target, message, code: String(code), arguments: processedArgs } }; }
这个方案完全无依赖,同时通过类型校验保证输出安全性,适合对依赖有严格限制的场景。
方案3:谨慎使用eval(仅信任内部API时)
如果API是完全可控的内部服务,可以使用eval直接解析JS对象字面量,但存在注入风险,不推荐对外暴露的服务使用:
import type { RestError } from './your-interface-path'; function parseRestErrorWithEval(rawErrorText: string): RestError { try { // 包裹为表达式避免语法错误 const parsed = eval(`(${rawErrorText.trim()})`); return { error: { target: parsed.error.target, message: parsed.error.message, code: String(parsed.error.code), arguments: parsed.error.arguments ?? undefined } }; } catch (err) { throw new Error(`解析错误失败: ${(err as Error).message}`); } }
内容的提问来源于stack exchange,提问作者Pradip
相关产品推荐
相关产品推荐

