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

如何在未知网关实现时定义TypeScript的UseCase类型?

TypeScript网关用例类型定义问题解决

问题代码与错误提示

用户编写的TypeScript代码如下:

class Gateway {}
class GatewayA extends Gateway { open() {} }
class GatewayB extends Gateway { }

type UseCase<T> = (...gateways: Gateway[]) => () => Promise<T>;

const gatewayA = new GatewayA;
const gatewayB = new GatewayB;

const fooUseCase: UseCase<boolean> = (gatewayA: GatewayA, gatewayB: GatewayB) => async () => {
  console.log(gatewayA);
  console.log(gatewayB);
  return true;
};

fooUseCase(gatewayA, gatewayB);

TypeScript抛出的类型兼容错误:

Type '(gatewayA: GatewayA, gatewayB: GatewayB) => () => Promise<true>' is not assignable to type 'UseCase<boolean>'.
  Types of parameters 'gatewayA' and 'gateways' are incompatible.
    Property 'open' is missing in type 'Gateway' but required in type 'GatewayA'.ts(2322)

需求说明:网关类继承自Gateway基类,部分子类会新增方法;不同用例需要传入不同的网关实例(如fooUseCase需要GatewayA和GatewayB,其他用例可能需要GatewayC),需要在预先不知道具体网关实现的情况下,正确定义UseCase类型。

解决方案

核心问题是原UseCase类型固定参数为Gateway[],但实际用例需要更具体的网关子类,而函数参数的类型兼容性是逆变的——接受子类的函数无法赋值给接受父类的函数类型(因为父类没有子类的专属方法)。

我们可以通过给UseCase添加泛型参数,让它适配任意网关类型组合:

方法1:适配固定组合的网关元组

如果每个用例需要的网关类型是固定的组合(比如[GatewayA, GatewayB]这种明确的顺序和类型),可以定义接受网关元组的泛型:

class Gateway {}
class GatewayA extends Gateway { open() {} }
class GatewayB extends Gateway { }

// 新增泛型G,约束为Gateway类型的元组
type UseCase<T, G extends Gateway[]> = (...gateways: G) => () => Promise<T>;

const gatewayA = new GatewayA;
const gatewayB = new GatewayB;

// 方式1:让TypeScript自动推导泛型参数
const fooUseCase = (gatewayA: GatewayA, gatewayB: GatewayB) => async () => {
  console.log(gatewayA);
  // 可以正常调用gatewayA的open方法
  gatewayA.open();
  console.log(gatewayB);
  return true;
};

// 方式2:显式指定泛型参数
const barUseCase: UseCase<boolean, [GatewayA, GatewayB]> = (gatewayA, gatewayB) => async () => {
  gatewayA.open();
  return true;
};

fooUseCase(gatewayA, gatewayB);
barUseCase(gatewayA, gatewayB);

方法2:适配任意数量的同类型网关

如果用例只需要任意数量的同一种网关子类,可以这样定义:

type UseCase<T, G extends Gateway = Gateway> = (...gateways: G[]) => () => Promise<T>;

// 示例:需要多个GatewayA的用例
const multiGatewayAUseCase: UseCase<boolean, GatewayA> = (...gateways) => async () => {
  gateways.forEach(g => g.open());
  return true;
};

原理说明

通过泛型参数G,我们让UseCase不再绑定固定的Gateway父类,而是可以适配任意符合Gateway子类约束的类型组合。TypeScript会自动推导每个用例对应的网关类型,既保证了类型安全(比如能正常调用子类的专属方法),又保留了灵活性,支持任意网关组合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 20:57:15