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

TypeScript与.proto类型互转指南:Nest.js gRPC服务场景

Nest.js gRPC服务间UserSettings类型的Protobuf映射与转换方案

问题背景

假设有两个基于Nest.js的gRPC服务Service A和Service B,需通过RPC在两者间传递UserSettings类型对象。Service A中的TypeScript类型定义如下:

enum Language {
  AMERICAN_ENGLISH = "en/us",
  BRITISH_ENGLISH = "en/uk",
  ITALIAN = "it"
}

type UserSettings = {
  theme: "dark" | "white";
  language: Language;
  device: string | number;
}

由于Protobuf支持的特性少于TypeScript,请问如何正确将该丰富类型映射到.proto文件?是否需要通过映射函数在Service A中将UserSettings转为UserSettingsProto、在Service B中再转回,或是有更优方案?


一、Protobuf类型映射方案

1. 枚举类型映射

Protobuf的枚举仅支持整数作为枚举值,所以要把TypeScript的字符串值枚举转成整数枚举,同时用注释标注对应的字符串映射:

syntax = "proto3";

enum Language {
  // 对应TypeScript的 "en/us"
  AMERICAN_ENGLISH = 0;
  // 对应TypeScript的 "en/uk"
  BRITISH_ENGLISH = 1;
  // 对应TypeScript的 "it"
  ITALIAN = 2;
}

2. 主题字段映射

TypeScript的"dark" | "white"字面量类型,在Protobuf里有两种处理方式:

  • 方案一:用枚举(更严格,避免非法值传入)
enum Theme {
  DARK = 0;
  WHITE = 1;
}
  • 方案二:直接用string类型,在业务逻辑层面校验值是否在允许范围内

3. Device字段映射

Protobuf不支持string | number这种联合类型,这里推荐两种严谨的处理方式:

  • 方案一:统一用string类型,发送方把数字转成字符串,接收方再根据业务需求转成数字
  • 方案二:用oneof字段,明确区分字符串和数字类型(更清晰,避免歧义)
message DeviceInfo {
  oneof device {
    string device_str = 1;
    int64 device_num = 2;
  }
}

完整.proto文件示例

结合上述映射规则,最终的user_settings.proto内容如下:

syntax = "proto3";

enum Language {
  AMERICAN_ENGLISH = 0;
  BRITISH_ENGLISH = 1;
  ITALIAN = 2;
}

enum Theme {
  DARK = 0;
  WHITE = 1;
}

message DeviceInfo {
  oneof device {
    string device_str = 1;
    int64 device_num = 2;
  }
}

message UserSettingsProto {
  Theme theme = 1;
  Language language = 2;
  DeviceInfo device = 3;
}

二、类型转换方案

1. 必须做类型转换

Protobuf生成的类型和原TypeScript业务类型结构完全不同,所以服务间调用时必须做类型转换:

  • Service A发送请求前:把业务用的UserSettings转成Protobuf生成的UserSettingsProto
  • Service B接收请求后:把UserSettingsProto转回业务用的UserSettings

2. 手动转换函数示例

Service A:业务类型转Protobuf类型

function toUserSettingsProto(settings: UserSettings): UserSettingsProto {
  const deviceInfo: DeviceInfo = {};
  if (typeof settings.device === 'string') {
    deviceInfo.deviceStr = settings.device;
  } else {
    deviceInfo.deviceNum = settings.device;
  }

  return {
    theme: settings.theme === 'dark' ? Theme.DARK : Theme.WHITE,
    language: Language[settings.language as keyof typeof Language],
    device: deviceInfo,
  };
}

Service B:Protobuf类型转业务类型

function toUserSettings(proto: UserSettingsProto): UserSettings {
  let device: string | number;
  if (proto.device?.deviceStr) {
    device = proto.device.deviceStr;
  } else if (proto.device?.deviceNum !== undefined) {
    device = proto.device.deviceNum;
  } else {
    throw new Error('无效的device值');
  }

  return {
    theme: proto.theme === Theme.DARK ? 'dark' : 'white',
    language: Language[proto.language] as Language,
    device,
  };
}

3. 更优方案:用工具简化转换

可以借助类库或Nest.js拦截器自动处理转换,减少重复代码:

  • 定义DTO类,用装饰器标注Protobuf字段的映射规则
  • 自定义Nest.js拦截器,在请求发送/接收阶段自动完成类型转换,不用手动调用转换函数

三、注意事项

  • Protobuf枚举默认值是第一个枚举项,要确保业务逻辑能处理默认值的情况
  • 使用oneof字段时,Protobuf会自动忽略多余的字段,转换时要保证只设置其中一个字段
  • 类型转换阶段要加入参数校验,避免非法值流入业务逻辑

内容的提问来源于stack exchange,提问作者Amel Amcë Muminovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 13:22:48