PAYATHON 2026

如何将 JSON objects 映射为 TypeScript arrays

支付阿杰

结论

代码中有两个问题:

  1. response.data.sales 是数组,但代码把它声明成了单个 ISales,随后又将整个数组作为一个元素放进 payments。这样一来,payments 就成了嵌套数组。
  2. 接口定义使用 id、amount、dateCreated,服务端返回的字段却是 Id、Amount、DateCreated。JavaScript 属性名区分大小写,item.amount 不会自动匹配 item.Amount。

处理时需要遍历 response.data.sales,明确映射字段名,并将日期字符串转换为 Date。

正确的映射方式

先定义应用内部使用的字段类型:

interface ISales {
  id: number;
  amount: number;
  dateCreated: Date;
}

再用 map() 转换响应数组:

const payments: ISales[] = response.data.sales.map((sale: any) => ({
  id: sale.Id,
  amount: sale.Amount,
  dateCreated: new Date(sale.DateCreated)
}));

转换后,就可以正常读取每条记录的属性:

payments.forEach((item) => {
  console.log("whole item", item);
  console.log("amount", item.amount);
});

此时 payments 的结构如下:

[
  {
    id: 2,
    amount: 500,
    dateCreated: Date
  },
  {
    id: 3,
    amount: 410,
    dateCreated: Date
  }
]

原来的代码为什么会得到 undefined

下面这行代码的类型声明有误:

const result: ISales = response.data.sales;

response.data.sales 是 Array(2),所以它应该是数组类型,而不是单个 ISales:

const result: ISales[] = response.data.sales;

接着执行:

payments.push(result);

会把整个 sales 数组作为一个元素压入 payments。最终结构类似:

[
  [
    { Id: 2, Amount: 500, DateCreated: "01/01/2019 00:00:00" },
    { Id: 3, Amount: 410, DateCreated: "01/20/2019 00:00:00" }
  ]
]

此时 item 是数组,不是单条销售记录,因此没有 amount 属性。

即使直接遍历原始响应,下面的代码也会返回 undefined:

response.data.sales.forEach((item: any) => {
  console.log(item.amount);
});

原因是服务端返回的字段名是大写开头的 Amount:

console.log(item.Amount); // 500、410

TypeScript 接口只负责静态类型检查。它不会在运行时重命名字段,也不会自动将字符串转换为 Date。

为响应数据单独定义类型

不建议长期使用 any。可以分别定义服务端响应类型和应用内部类型:

interface SalesResponseItem {
  Id: number;
  Amount: number;
  DateCreated: string;
}

interface ISales {
  id: number;
  amount: number;
  dateCreated: Date;
}

完整的转换代码如下:

const sales: SalesResponseItem[] = response.data.sales;

const payments: ISales[] = sales.map((sale) => ({
  id: sale.Id,
  amount: sale.Amount,
  dateCreated: new Date(sale.DateCreated)
}));

这样既能准确描述接口返回的数据,也能让应用内部统一使用 TypeScript 中常见的 camelCase 属性名。

如果不需要转换字段名

也可以让接口直接匹配服务端返回的数据:

interface ISales {
  Id: number;
  Amount: number;
  DateCreated: string;
}

const payments: ISales[] = response.data.sales;

payments.forEach((item) => {
  console.log(item.Amount);
});

这种写法代码更少,但应用中必须始终使用 Id、Amount 和 DateCreated,而且 DateCreated 依然是字符串。

日期转换的注意事项

"01/20/2019 00:00:00" 不是标准的 ISO 8601 格式,不同浏览器或运行环境可能会得到不同的解析结果。如果可以调整接口,建议让服务端返回标准格式,例如:

2019-01-20T00:00:00Z

然后再进行转换:

dateCreated: new Date(sale.DateCreated)

如果无法修改服务端格式,就应该按照明确的规则拆分并解析日期,不要假定所有运行环境都能正确识别 MM/DD/YYYY HH:mm:ss。

备注:内容仅供参考。