如何用OOP在TypeScript前端优雅处理API数据转换?
优雅处理TypeScript中API数据到应用内数据的转换
问题场景
我用TypeScript开发Web前端,需要把API返回的BlogpostAPI对象转换成应用内使用的BlogpostApp对象,类型定义如下:
API返回数据结构:
interface BlogpostAPI { title: string; content: string; createdAt: string; // ...其他属性 }
应用内需要的数据结构:
interface BlogpostApp { title: string; content: BlogpostBlock[]; createdAt: Date; // ...其他需要转换的属性 } type BlogpostBlock = { role: 'body' | 'header'; text: string; }
我希望代码整洁,尝试了两种方案但都有顾虑:
方案1:用实现BlogpostApp的类处理转换
class Blogpost implements BlogpostApp { title: string; content: BlogpostBlock[]; createdAt: Date; constructor(post: BlogpostApp) { this.title = post.title; this.content = post.content; this.createdAt = post.createdAt; } static fromBlogpostAPI(post: BlogpostAPI): Blogpost { const content: BlogpostBlock[] = post.content.split('\n').map((s, idx) => { if (idx === 0) return { text: s, role: 'header' }; return { text: s, role: 'body' }; }); const createdAt: Date = new Date(post.createdAt); return new Blogpost({ title: post.title, content, createdAt }); } }
顾虑:
BlogpostApp接口仅用于构造函数参数的可读性,显得多余Blogpost类和BlogpostApp接口属性完全一致,存在冗余
方案2:独立的转换类
class BlogpostTransformer { constructor() {} static transform(post: BlogpostAPI): BlogpostApp { const content: BlogpostBlock[] = post.content.split('\n').map((s, idx) => { if (idx === 0) return { text: s, role: 'header' }; return { text: s, role: 'body' }; }); const createdAt: Date = new Date(post.createdAt); return { title: post.title, content, createdAt } as BlogpostApp; } }
顾虑:每个API对象都创建仅含单个静态方法的类,架构上不够简洁合理
推荐的优雅解决方案
1. 函数式转换(轻量首选)
直接定义纯函数负责转换,无需类结构,简洁且符合函数式编程风格:
function transformBlogpostAPI(post: BlogpostAPI): BlogpostApp { const content = post.content.split('\n').map((text, idx) => ({ text, role: idx === 0 ? 'header' : 'body' as const })); return { ...post, // 直接复用无需转换的属性 content, createdAt: new Date(post.createdAt) }; }
优势:代码简洁、无冗余,纯函数易于测试和复用,适合简单的转换场景。
2. 复用接口的实体类(适合带业务逻辑的场景)
如果应用内的Blogpost需要包含业务方法,可以让类直接接收BlogpostAPI构造,去掉冗余的BlogpostApp接口:
class Blogpost { title: string; content: BlogpostBlock[]; createdAt: Date; constructor(post: BlogpostAPI) { this.title = post.title; this.content = post.content.split('\n').map((text, idx) => ({ text, role: idx === 0 ? 'header' : 'body' as const })); this.createdAt = new Date(post.createdAt); // 其他属性的转换逻辑 } // 可以添加业务方法,比如: getFormattedCreatedAt(): string { return this.createdAt.toLocaleDateString(); } }
优势:去掉冗余接口,类既承载数据结构又负责转换和业务逻辑,适合需要对数据进行操作的场景。
3. 通用Transformer模式(适合大量转换场景)
如果有很多API转换需求,可以定义通用的Transformer接口,统一转换规范:
interface Transformer<From, To> { transform(data: From): To; } class BlogpostTransformer implements Transformer<BlogpostAPI, BlogpostApp> { transform(post: BlogpostAPI): BlogpostApp { const content = post.content.split('\n').map((text, idx) => ({ text, role: idx === 0 ? 'header' : 'body' as const })); return { ...post, content, createdAt: new Date(post.createdAt) }; } }
优势:统一转换接口,扩展性强,适合大型项目中统一管理所有API数据转换逻辑,便于维护和测试。
内容的提问来源于stack exchange,提问作者Geemishu
相关产品推荐
相关产品推荐

