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

如何让TypeScript函数接收任意泛型参数的Foo类型数组?

问题:实现可接收任意泛型参数Foo实例数组的process函数

用户代码如下:

type Foo<T = unknown> = {
  bar: T
  bar2: (v: T) => T
}

const f1 : Foo<number> = {
  bar: 42,
  bar2: (v: number) => 5 + v
}

const f2 : Foo<string> = {
  bar: "hello",
  bar2: (v: string) => v
}

// 意图:接收任意Foo实例或string的数组
function process(arr: (Foo|string)[]) {
 ...
}

process([f1, f2]) // 报错

运行时出现的错误:

Type 'Foo<number>' is not assignable to type 'string | Foo<unknown>'.
  Type 'Foo<number>' is not assignable to type 'Foo<unknown>'.
    Types of property 'bar2' are incompatible.
      Type '(v: number) => number' is not assignable to type '(v: unknown) => unknown'.
        Types of parameters 'v' and 'v' are incompatible.
          Type 'unknown' is not assignable to type 'number'

错误原因

TypeScript中函数参数遵循逆变规则:如果类型A是类型B的子类型,那么(B) => any是(A) => any的子类型。这里Foo<unknown>的bar2要求参数是unknown(可接受任意类型),而Foo<number>的bar2只接受number类型。如果把Foo<number>当作Foo<unknown>使用,调用bar2时可能传入非number值,会导致运行时错误,因此TypeScript禁止这种赋值。

解决方案

方案1:使用any兼容任意Foo

通过Foo<any>绕过类型检查,any可以兼容所有泛型参数的Foo实例:

function process(arr: (Foo<any> | string)[]) {
  // 函数逻辑
}

process([f1, f2]) // 正常运行

注意:any会关闭类型检查,若在process内部错误调用bar2传错参数,无法得到编译期提示。

方案2:泛型函数+联合类型

将process定义为泛型函数,指定泛型参数为所有Foo实例的泛型参数联合类型:

function process<T extends unknown>(arr: Array<Foo<T> | string>) {
  // 可通过类型守卫处理不同类型的元素
  arr.forEach(item => {
    if ('bar' in item) {
      // item 类型为 Foo<T>
      console.log(item.bar);
    } else {
      // item 类型为 string
      console.log(item);
    }
  });
}

// 调用时手动指定联合类型
process<number | string>([f1, f2])

方案3:类型断言(不推荐)

在调用时将Foo<number>和Foo<string>断言为Foo<unknown>,强制通过类型检查:

function process(arr: (Foo<unknown> | string)[]) {
  // 函数逻辑
}

process([f1 as Foo<unknown>, f2 as Foo<unknown>])

此方法会丢失类型安全性,仅在确定process内部不会错误调用bar2时使用。

内容的提问来源于stack exchange,提问作者Guid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:35:16