👽

【gRPC】ts-protoで自動生成されるTypeScriptの型のまとめ

2023/03/12に公開

はじめに

ts-protoを使うとprotoファイルから型(interface)を自動生成してくれて大変便利です。

若干癖というか想定した通りの型が生成されないことがあったので、protoファイルと自動生成されたコードを見比べられるようにまとめていきます。

環境

タイトル バージョン
Node.js v18.14.2
npm 9.5.0
ts-proto 1.140.0

スカラ型

Protocol Buffersではスカラ型として15種類定義されています。
こちらのprotoファイルでの定義と生成後のtsファイルを見比べてみます。

sample.proto
message SampleMessage {
  double double = 1;
  float float = 2;
  int32 int32 = 3;
  int64 int64 = 4;
  uint32 uint32 = 5;
  uint64 uint64 = 6;
  sint32 sint32 = 7;
  sint64 sint64 = 8;
  fixed32 fixed32 = 9;
  fixed64 fixed64 = 10;
  sfixed32 sfixed32 = 11;
  sfixed64 sfixed64 = 12;
  bool bool = 13;
  string string = 14;
  bytes bytes = 15;
}
sample.ts
export interface SampleMessage {
  double: number;
  float: number;
  int32: number;
  int64: number;
  uint32: number;
  uint64: number;
  sint32: number;
  sint64: number;
  fixed32: number;
  fixed64: number;
  sfixed32: number;
  sfixed64: number;
  bool: boolean;
  string: string;
  bytes: Uint8Array;
}

数値はnumber型、bool値はboolean型、文字列はstring、bytesはUint8Array(8ビット符号なし整数値の配列)になります。

MessageTypeのネストやrepeatedによる配列構造

MessageTypeはネストすることができます。

また、FieldTypeの頭にrepeatedを付けることで配列を定義することができます。
こちらの例を見ていきます。

sample.proto
message Fuga {
  int32 fuga = 1;
  string fugafuga = 2;  
}

message NestedMessage {
  message Hoge {
    string hoge = 1;
    string hogehoge = 2;
    repeated string string = 3;
  }
  repeated Fuga fuga = 4;
}
sample.ts
export interface Fuga {
  fuga: number;
  fugafuga: string;
}

export interface NestedMessage {
  fuga: Fuga[];
}

export interface NestedMessage_Hoge {
  hoge: string;
  hogehoge: string;
  string: string[];
}

repeatedがついたものは配列で表現されており想定通りですが、MessageTypeをネストさせても別のinterfaceとして定義されるみたいですね。

注意として、rpc FindOne (RequestById) returns (repeated HogeResponse) {}みたいにreturnsに直接オブジェクトの配列指定ができないので、今回の例だとデータとしては下記のようになります。

index.ts
const data: NestedMessage = {
  fuga: [
    {
      fuga: 1,
       fugafuga: "ふがふが"
    },
    {
      fuga: 2,
      fugafuga: "フガフガ"
    },
  ]
}

enum型

これは良くも悪くも普通にenumになり、UNRECOGNIZED(未認識)が自動的に付与されます。

sample.proto
enum Enumerations {
  ENUM_ONE = 0;
  ENUM_TWO = 1;
  ENUM_THREE = 2;
}
sample.ts
export enum Enumerations {
  ENUM_ONE = 0,
  ENUM_TWO = 1,
  ENUM_THREE = 2,
  UNRECOGNIZED = -1,
}

https://typescriptbook.jp/reference/values-types-variables/enum/enum-problems-and-alternatives-to-enums

any型

試しにany型も使ってみましょう。

sample.proto
import "google/protobuf/any.proto";

message AnyType {
  google.protobuf.Any any = 1;
}
sample.ts
export interface AnyType {
  any: Any | undefined;
}

oneof

oneofを使用することでPartial<T>みたいに全てoptionalにしてくれます。

sample.proto
message OneOf {
  oneof piyo_oneof {
    string piyo = 1;
    string piyopiyo = 2;
  }
}
sample.ts
export interface OneOf {
  piyo?: string | undefined;
  piyopiyo?: string | undefined;
}

map

こちらも大体予想通りという感じです。

sample.proto
message Foo {
  map<string, int32> foo = 1;
}
sample.ts
export interface Foo {
  foo: { [key: string]: number };
}

さいごに

あんまり試したことないパターンもあったので勉強になりました。

Discussion