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

如何用TypeScript泛型优化NestJS微服务嵌套对象的强类型调用

NestJS微服务通信的强类型约束优化

类型定义

我正在为NestJS微服务通信实现强类型约束,思路是通过一个对象定义所有微服务调用方式,包含对应的输入(args)和返回(result)类型,具体定义如下:

export type CompanyExpectedRoleAndCmd = {
      'company': {
        'find-unique': {
          input: number;
          result: string;
        };
        'find-many': {
          input: string;
          result: number;
        };
    
        'find-update': {
          input: string | number;
          result: number;
        };
      };
      'company-employee': {
        'find-unique': {
          input: string;
          result: Array<string>;
        };
      };
    };
    
export type EmployeeExpectedRoleAndCmd = {
      'employee': {
        'find-unique': {
          input: string;
          result: number;
        };
        'find-many': {
          input: number;
          result: string;
        };
    
        'find-update': {
          input: string;
          result: Array<number>;
        };
      };
      'employee-provider': {
        'find-unique': {
          input: number;
          result: Array<string>;
        };
      };
    };


export type ServiceTCP = {
  company: CompanyExpectedRoleAndCmd;
  employee: EmployeeExpectedRoleAndCmd;
  // 更多服务...
};

现有实现逻辑

ServiceTCP定义了每个微服务的作用域,每个键对应的值是该微服务的所有交互方式。我已经实现了一套基础的强类型定义:

export type ServiceKeys = keyof ServiceTCP;

export type ServiceRoleAndCmd<T extends ServiceKeys> = keyof ServiceTCP[T];

export type ServiceRole<
  T extends ServiceKeys,
  K extends ServiceRoleAndCmd<T>,
> = keyof ServiceTCP[T][K];

export type ServiceCmd<
  T extends ServiceKeys,
  K extends ServiceRoleAndCmd<T>,
  C extends ServiceRole<T, K>,
> = keyof ServiceTCP[T][K][C];

export type ServiceInput<
  T extends ServiceKeys,
  K extends ServiceRoleAndCmd<T>,
  C extends ServiceRole<T, K>,
> = T extends keyof ServiceTCP
  ? K extends keyof ServiceTCP[T]
    ? C extends keyof ServiceTCP[T][K]
      ? ServiceTCP[T][K][C] extends { input: infer I }
        ? I
        : never
      : never
    : never
  : never;

export type ServiceResult<
    T extends ServiceKeys,
    K extends ServiceRoleAndCmd<T>,
    C extends ServiceRole<T, K>,
    > = T extends keyof ServiceTCP
    ? K extends keyof ServiceTCP[T]
        ? C extends keyof ServiceTCP[T][K]
            ? ServiceTCP[T][K][C] extends { result: infer R }
                ? R
                : never
            : never
        : never
    : never;

export type Pattern<
  T extends ServiceKeys,
  R extends ServiceRoleAndCmd<T>,
  C extends ServiceRole<T, R>,
> = {
  role: R;
  cmd: C;
};

存在的问题

这些类型定义可以正常工作,但TypeScript使用效率不高。比如我实现的微服务通信函数sendMessageTCP:

const sendMessageTCP = async <
  T extends ServiceKeys,
  R extends ServiceRoleAndCmd<T>,
  C extends ServiceRole<T, R>
>(
  pattern: Pattern<T, R, C>,
  input: ServiceInput<T, R, C>,
): Promise<ServiceResult<T, R, C>> => {
  /// 示例中手动类型转换
  return {} as ServiceResult<T, R, C>;
};

调用时必须指定每一层泛型参数,冗余度很高:

const result = sendMessageTCP<'company', 'company', 'find-many'>(
    { role: 'company', cmd: 'find-many' },
    'string',
);
// result 类型为 string

我希望优化后只需指定服务键(如'company'),就能自动推导其他类型,同时保持输入、返回值的强类型约束,调用方式如下:

const result = sendMessageTCP<'company'>(
    { role: 'company', cmd: 'find-many' },
    'string',
);
// result 类型为 string

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:46:04