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

如何从数组联合类型中正确推导分发式类型以解决TypeScript类型不兼容问题

如何从数组联合类型中正确推导分发式类型以解决TypeScript类型不兼容问题

我理解你的痛点——你无法修改getData的返回类型(它返回Foo[] | Bar[]而非(Foo | Bar)[]),但TypeScript无法自动将数组联合类型推断为元素联合的数组,导致泛型函数myFunc出现类型不兼容错误,且renderRow的参数被错误推断为Foo而非Foo | Bar。下面结合你提到的分布式条件类型,给出三种针对性解决方案:

方案一:手动指定泛型参数为元素联合类型

这是最直接的临时解决办法:直接给myFunc指定泛型参数为Foo | Bar,让TypeScript将data的类型视为(Foo | Bar)[](而Foo[] | Bar[]本身是可以安全赋值给(Foo | Bar)[]的,因为每个数组元素都属于Foo | Bar范畴):

type Foo = { id: string; someFooProp: string };
type Bar = { id: string; someBarProp: string };

function getData(): Foo[] | Bar[] {
  const bars: Bar[] = [{id: '1', someBarProp: '1'}];
  return bars;
}

type MyFuncProps<T> = {
  data: T[];
  renderRow: (data: T) => string;
};

function myFunc<T>(props: MyFuncProps<T>) {
  return { /* iterate over data and render*/ };
}

// 手动指定泛型参数为 Foo | Bar
myFunc<Foo | Bar>({
  data: getData(), // 类型错误消失,TypeScript 允许 Foo[] | Bar[] 赋值给 (Foo | Bar)[]
  renderRow: (dataItem) => {
    if ('someBarProp' in dataItem) return 'this is bar';
    if ('someFooProp' in dataItem) return 'this is foo';
    return '';
  }
});

优点是快速见效,缺点是需要手动维护Foo | Bar这个联合类型,后续类型变更时需要同步更新。

方案二:用分布式条件类型自动推导元素联合类型

如果你希望TypeScript自动从数组联合类型中提取元素的联合,可以借助分布式条件类型实现自动化推导,无需手动指定泛型:

步骤1:定义元素类型提取工具

先创建一个工具类型,它会利用TypeScript的类型推断和分布式特性,从数组类型中提取元素类型:

// 分布式条件类型:从数组类型中提取元素类型
type ElementType<T> = T extends (infer U)[] ? U : never;

当T是联合类型(比如Foo[] | Bar[])时,TypeScript会自动将联合类型拆分,分别对每个成员执行条件判断,最终返回Foo | Bar。

步骤2:调整泛型函数的约束

修改myFunc的泛型定义,让它接受任意数组类型,并自动绑定renderRow的参数类型为数组元素的联合类型:

type Foo = { id: string; someFooProp: string };
type Bar = { id: string; someBarProp: string };

function getData(): Foo[] | Bar[] {
  const bars: Bar[] = [{id: '1', someBarProp: '1'}];
  return bars;
}

// 提取数组元素类型的工具类型
type ElementType<T> = T extends (infer U)[] ? U : never;

// 重新定义泛型函数:接受任意数组类型 T,自动推导元素类型
function myFunc<T extends any[]>(props: {
  data: T;
  renderRow: (dataItem: ElementType<T>) => string;
}) {
  return { /* iterate over data and render*/ };
}

myFunc({
  data: getData(), // 自动推导 T 为 Foo[] | Bar[]
  renderRow: (dataItem) => { // dataItem 被正确推断为 Foo | Bar
    if ('someBarProp' in dataItem) return 'this is bar';
    if ('someFooProp' in dataItem) return 'this is foo';
    return '';
  }
});

核心原理

ElementType<T>是典型的分布式条件类型:当T为联合类型时,TypeScript会将联合类型拆分,对每个成员单独执行条件判断,最终将结果合并为新的联合类型。对于Foo[] | Bar[],它会被拆分为:

ElementType<Foo[]> | ElementType<Bar[]> = Foo | Bar

这就自动从数组联合类型中提取出了元素的联合类型,完美匹配你的需求。

方案三:保留原MyFuncProps结构的优化

如果你想保留原有的MyFuncProps泛型结构,可以通过包装泛型参数实现自动推导:

type Foo = { id: string; someFooProp: string };
type Bar = { id: string; someBarProp: string };

function getData(): Foo[] | Bar[] {
  const bars: Bar[] = [{id: '1', someBarProp: '1'}];
  return bars;
}

type ElementType<T> = T extends (infer U)[] ? U : never;

// 调整 MyFuncProps 接受数组类型,通过 ElementType 推导元素类型
type MyFuncProps<TArr> = {
  data: TArr;
  renderRow: (data: ElementType<TArr>) => string;
};

function myFunc<TArr extends any[]>(props: MyFuncProps<TArr>) {
  return { /* iterate over data and render*/ };
}

myFunc({
  data: getData(),
  renderRow: (dataItem) => {
    if ('someBarProp' in dataItem) return 'this is bar';
    if ('someFooProp' in dataItem) return 'this is foo';
    return '';
  }
});

这个方案和方案二原理一致,只是将类型定义拆分成了MyFuncProps,保持了你原有的代码结构。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 11:58:06