TypeScript中如何解决联合类型数组reduce的类型兼容问题
类型错误修复:解决reduce方法的联合类型签名不兼容问题
问题代码
type TDeliveriesStatsDatum<TName = string> = {name: TName; value: number}; type TDeliveriesStatsData<TName = string> = TDeliveriesStatsDatum<TName>[]; interface IDeliveriesStats { meta: { delivery_count: number; transport_count: number; }; progress: TDeliveriesStatsData<"done" | "not_done" | "cancelled">; disputes: TDeliveriesStatsData<"disputed" | "undisputed">; loading_punctuality: TDeliveriesStatsData<"ontime" | "delayed" | "unknown">; unloading_punctuality: TDeliveriesStatsData<"ontime" | "delayed" | "unknown">; cmr_document: TDeliveriesStatsData<"done_with_cmr_document" | "done_without_cmr_document">; non_cmr_document: TDeliveriesStatsData< "done_with_non_cmr_document" | "done_without_non_cmr_document" >; loading_delay: TDeliveriesStatsData; unloading_delay: TDeliveriesStatsData; loading_duration: TDeliveriesStatsData; unloading_duration: TDeliveriesStatsData; } type DeliveriesStatsKeys = "progress" | "disputes" | "cmr_document" | "non_cmr_document"; type TPieChartData<T extends DeliveriesStatsKeys> = { augmentedData: {name: string, value: number, dataKey: string, fill: string}[] } & { [K in IDeliveriesStats[T][0]["name"]]: number; }; export const formatPieChartData = <K extends DeliveriesStatsKeys>( data: IDeliveriesStats[K] ): TPieChartData<K> => { return data.reduce( (acc: TPieChartData<K>, datum: IDeliveriesStats[K][0]) => { acc[datum.name] = datum.value; acc.augmentedData.push({ ...datum, dataKey: datum.name, fill: "colorsAndLabelsByDataKey[datum.name].fill," }); return acc; }, {augmentedData: []} as TPieChartData<K> ); };
编译错误
This expression is not callable. Each member of the union type '{ (callbackfn: (previousValue: TDeliveriesStatsDatum<"done" | "not_done" | "cancelled">, currentValue: TDeliveriesStatsDatum<"done" | "not_done" | "cancelled">, currentIndex: number, array: TDeliveriesStatsDatum<...>[]) => TDeliveriesStatsDatum<...>): TDeliveriesStatsDatum<...>; (callbackfn: (previousValue: TDeliver...' has signatures, but none of those signatures are compatible with each other.
解决方案
错误根源在于:当K是DeliveriesStatsKeys的联合类型时,IDeliveriesStats[K]会成为不同数组类型的联合,而reduce方法的签名在联合类型下无法统一匹配。我们可以通过以下两步调整修复问题:
1. 优化TPieChartData类型定义
用Record工具类型替代手动索引签名,让类型更清晰且符合TS规范:
type TPieChartData<T extends DeliveriesStatsKeys> = { augmentedData: {name: string, value: number, dataKey: string, fill: string}[] } & Record<IDeliveriesStats[T][0]["name"], number>;
2. 调整reduce的初始值与类型断言
由于初始值不包含TPieChartData<K>的所有属性,先用Partial标记可选,最后再断言为完整类型,同时对datum.name做类型守卫确保索引合法:
export const formatPieChartData = <K extends DeliveriesStatsKeys>( data: IDeliveriesStats[K] ): TPieChartData<K> => { const result = data.reduce( (acc: Partial<TPieChartData<K>>, datum) => { // 断言datum.name为acc的合法键 acc[datum.name as keyof typeof acc] = datum.value; // 确保augmentedData存在后再push acc.augmentedData = acc.augmentedData || []; acc.augmentedData.push({ ...datum, dataKey: datum.name, fill: "colorsAndLabelsByDataKey[datum.name].fill," }); return acc; }, {augmentedData: []} as Partial<TPieChartData<K>> ); // 断言结果为完整的TPieChartData<K>(确保输入数据包含所有必要name值) return result as TPieChartData<K>; };
额外优化:缩小泛型约束范围(可选)
如果不需要支持联合类型的K,可以保持现有泛型约束,TS会自动为每个具体的K推断出正确的类型,无需额外处理联合类型冲突。
内容的提问来源于stack exchange,提问作者Corentin S.
相关产品推荐
相关产品推荐

