如何将 JSON objects 映射为 TypeScript arrays
支付阿杰
结论
代码中有两个问题:
response.data.sales是数组,但代码把它声明成了单个ISales,随后又将整个数组作为一个元素放进payments。这样一来,payments就成了嵌套数组。- 接口定义使用
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。
备注:内容仅供参考。
