You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:02:44