Zero-codegen, no-compile TypeScript
typeinference from protobufmessages.
protobuf-ts-types lets you define language-agnostic message types in proto format, then infers TypeScript types from them with no additional codegen.
Try on github.dev | View on CodeSandbox
Warning
Proof of concept, not production ready. See Limitations below for more details.
In short, aggressive use of TypeScript's template literal types. Annotated example from the source:
// Pass the proto string you want to infer `message` names from as a generic parameter
type MessageNames<Proto extends string> =
  // Infer `message` parts using template literal type
  WrapWithNewlines<Proto> extends `${string}${Whitespace}message${Whitespace}${infer MessageName}${OptionalWhitespace}{${string}}${infer Rest}`
    ? // Recursively infer remaining message names
      [MessageName, ...MessageNames<Rest>]
    : [];See more in src/proto.ts.
First, install the package.
npm install https://github.com/nathanhleung/protobuf-ts-types
Then, use it in TypeScript.
import { pbt } from "protobuf-ts-types";
const proto = `
    syntax = "proto3";
    message Person {
      string name = 1;
      int32 id = 2;
      bool is_ceo = 3;
      optional string description = 4;
    }
    message Group {
        string name = 1;
        repeated Person people = 2;
    }
`;
// `Proto` is a mapping of message names to message types, inferred from the
// `proto` source string above.
type Proto = pbt.infer<typeof proto>;
type Person = Proto["Person"];
type Person2 = pbt.infer<typeof proto, "Person">;
// `Person` and `Person2` are the same type:
// ```
// {
//     name: string;
//     id: number;
//     is_ceo: boolean;
//     description?: string;
// }
// ```
type Group = pbt.infer<typeof proto, "Group">;
function greetPerson(person: Person) {
  console.log(`Hello, ${person.name}!`);
  if (person.description) {
    console.log(`${person.description}`);
  } else {
    console.log("(no description)");
  }
}
function greetGroup(group: Group) {
  console.log(`=========${"=".repeat(group.name.length)}===`);
  console.log(`= Hello, ${group.name}! =`);
  console.log(`=========${"=".repeat(group.name.length)}===`);
  for (const person of group.people) {
    greetPerson(person);
    console.log();
  }
}
// If the structure of the `Group` or any of the individual `Person`s does not
// match the type, TypeScript will show an error.
greetGroup({
  name: "Hooli",
  people: [
    {
      name: "Gavin Belson",
      id: 0,
      is_ceo: true,
      description: "CEO of Hooli",
    },
    {
      name: "Richard Hendricks",
      id: 1,
      is_ceo: true,
      description: "CEO of Pied Piper",
    },
    {
      name: "Dinesh Chugtai",
      id: 2,
      is_ceo: false,
      description: "Software Engineer",
    },
    {
      name: "Jared Dunn",
      id: 3,
      is_ceo: false,
    },
  ],
});
// Output:
// ```
// =================
// = Hello, Hooli! =
// =================
// Hello, Gavin Belson!
// CEO of Hooli
// Hello, Richard Hendricks!
// CEO of Pied Piper
// Hello, Dinesh Chugtai!
// Software Engineer
// Hello, Jared Dunn!
// (no description)
// ```- If not using inline (i.e., literals in TypeScript) proto stringsas const, probably requires ats-patchcompiler patch to import.protofiles until microsoft/TypeScript#42219 is resolved
- services and- rpcs are not supported (only- messages)
- oneofand- mapfields are not supported
- imports are not supported (for now, concatenate)
Top-level exported namespace.
import { pbt } from "protobuf-ts-types";
Given a proto source string, infers the types of the messages in the source.
- If MessageNameis an empty string, the returned type is a mapping from message names to message types.
- If MessageNameis a knownmessage, the returned type is the inferred type of the givenMessageName.
- If MessageNameis not a knownmessage, the returned type isnever.
 
            