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

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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:05:23