protoc-gen-ts-proto
v1.0.0
Published
Zero-dependency proto3 parser and TypeScript code generator — parse .proto files, generate interfaces, enums, service stubs, and gRPC client types
Maintainers
Readme
protoc-gen-ts-proto
Zero-dependency proto3 parser and TypeScript code generator.
Parse .proto files into an AST, generate TypeScript interfaces, enums, gRPC client/server stubs, and service definitions — or emit .proto source from plain JavaScript objects.
npm install protoc-gen-ts-protoQuick start
import { parseProto, generateTs } from 'protoc-gen-ts-proto';
const source = `
syntax = "proto3";
package acme.v1;
message User {
string id = 1;
string name = 2;
int32 age = 3;
repeated string tags = 4;
}
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (stream User);
}
`;
const ast = parseProto(source);
const ts = generateTs(ast);
console.log(ts);Output:
// Code generated by protoc-gen-ts-proto. DO NOT EDIT.
export interface User {
id?: string;
name?: string;
age?: number;
tags: string[];
}
export interface UserServiceClient {
getUser(request: GetUserRequest): Promise<User>;
listUsers(request: ListUsersRequest): AsyncIterable<User>;
}
export interface UserServiceServer {
getUser(request: GetUserRequest): Promise<User>;
listUsers(request: ListUsersRequest): AsyncIterable<User>;
}
export const userServiceServiceDefinition = {
name: "UserService",
methods: {
getUser: { name: "GetUser", requestType: "GetUserRequest", responseType: "User", clientStreaming: false, serverStreaming: false },
listUsers: { name: "ListUsers", requestType: "ListUsersRequest", responseType: "User", clientStreaming: false, serverStreaming: true },
},
} as const;parseProto(source)
Parse a proto3 source string into a typed AST. Supports:
syntax,package,importmessage(nested messages, nested enums,oneof,map<K,V>,repeated,optional)enumservice/rpc(unary, server-streaming, client-streaming, bidi-streaming)option(file, message, field, service, rpc)reserved,extensions, block comments, line comments
import { parseProto } from 'protoc-gen-ts-proto';
const ast = parseProto(protoSource);
// {
// syntax: 'proto3',
// package: 'acme.v1',
// imports: [{ path: 'google/protobuf/timestamp.proto', modifier: '' }],
// messages: [...],
// enums: [...],
// services: [...],
// options: {},
// }generateTs(ast, opts?)
Generate TypeScript source from a parsed AST.
import { generateTs } from 'protoc-gen-ts-proto';
const ts = generateTs(ast, {
useOptionalFields: true, // `field?: Type` (default true)
int64AsString: true, // int64 → string (default true)
promiseStyle: true, // client methods return Promise (default true)
generateClients: true, // emit *Client interfaces (default true)
generateServers: true, // emit *Server interfaces (default true)
generateDefinitions: true, // emit *ServiceDefinition consts (default true)
banner: '// Code generated by protoc-gen-ts-proto. DO NOT EDIT.',
});Streaming RPCs
| Proto | TypeScript |
|---|---|
| rpc M (A) returns (B) | m(req: A): Promise<B> |
| rpc M (A) returns (stream B) | m(req: A): AsyncIterable<B> |
| rpc M (stream A) returns (B) | m(stream: AsyncIterable<A>): Promise<B> |
| rpc M (stream A) returns (stream B) | m(stream: AsyncIterable<A>): AsyncIterable<B> |
Type mapping
Proto scalars → TypeScript
| Proto | TypeScript |
|---|---|
| string | string |
| bool | boolean |
| int32, uint32, float, double | number |
| int64, uint64, sint64, fixed64 | string (or bigint with int64AsString: false) |
| bytes | Uint8Array |
Well-known types
| Proto | TypeScript |
|---|---|
| google.protobuf.Timestamp | string |
| google.protobuf.Any | unknown |
| google.protobuf.Struct | Record<string, unknown> |
| google.protobuf.Empty | Record<string, never> |
| google.protobuf.StringValue | string |
| google.protobuf.BoolValue | boolean |
| … | … |
import { protoTypeToTs, protoScalarToTs } from 'protoc-gen-ts-proto';
protoTypeToTs('string') // 'string'
protoTypeToTs('int64') // 'string'
protoTypeToTs('int64', { int64AsString: false }) // 'bigint'
protoTypeToTs('map<string,int32>') // 'Record<string, number>'
protoTypeToTs('google.protobuf.Timestamp') // 'string'
protoTypeToTs('MyMessage') // 'MyMessage'
protoScalarToTs('bool') // 'boolean'
protoScalarToTs('bytes') // 'Uint8Array'
protoScalarToTs('Foo') // null (not a scalar)Name helpers
import { toPascalCase, toCamelCase, toSnakeCase } from 'protoc-gen-ts-proto';
toPascalCase('get_user_by_id') // 'GetUserById'
toCamelCase('get_user_by_id') // 'getUserById'
toSnakeCase('GetUserById') // 'get_user_by_id'AST traversal
import { collectMessages, collectEnums, collectServices, collectImports } from 'protoc-gen-ts-proto';
// Flat list including nested messages, each with fullName
const messages = collectMessages(ast);
// [{ name: 'User', fullName: 'User', fields: [...] }, { name: 'Address', fullName: 'User.Address', ... }]
const enums = collectEnums(ast);
const services = collectServices(ast);
const imports = collectImports(ast); // ['google/protobuf/timestamp.proto', ...]Field defaults
import { fieldDefault } from 'protoc-gen-ts-proto';
fieldDefault({ type: 'string', label: '' }) // "''"
fieldDefault({ type: 'bool', label: '' }) // "false"
fieldDefault({ type: 'int32', label: '' }) // "0"
fieldDefault({ type: 'MyMessage', label: '' }) // "undefined"
fieldDefault({ type: 'string', label: 'repeated' }) // "[]"
fieldDefault({ type: 'string', label: 'map' }) // "{}"Validation
import { isValidProtoIdent, isValidProtoType, validateAst } from 'protoc-gen-ts-proto';
isValidProtoIdent('myField') // true
isValidProtoIdent('123bad') // false
isValidProtoType('string') // true
isValidProtoType('acme.v1.User') // true
const errors = validateAst(ast);
// [] or ['Message "Foo": invalid field name "123"', ...]buildProto(descriptor)
Generate .proto source from a plain JavaScript object:
import { buildProto } from 'protoc-gen-ts-proto';
const proto = buildProto({
syntax: 'proto3',
pkg: 'acme.v1',
imports: ['google/protobuf/timestamp.proto'],
messages: [
{
name: 'User',
fields: [
{ type: 'string', name: 'id', number: 1 },
{ type: 'string', name: 'name', number: 2 },
{ type: 'google.protobuf.Timestamp', name: 'created_at', number: 3 },
],
},
],
services: [
{
name: 'UserService',
rpcs: [
{ name: 'GetUser', inputType: 'GetUserRequest', outputType: 'User' },
{ name: 'ListUsers', inputType: 'ListUsersRequest', outputType: 'User', serverStream: true },
],
},
],
});CommonJS
const { parseProto, generateTs, protoTypeToTs } = require('protoc-gen-ts-proto');License
MIT
