typia.protobuf.message โ emit a .proto schema from a TypeScript type
If you only encode and decode within TypeScript, you donโt need this โ the encode and decode functions analyze your type internally and produce a wire-compatible binary form.
typia.protobuf.message<T>() is for the other case: when you need to share the schema with a service written in another language (Go, Python, โฆ). It returns the .proto text that any Protocol Buffer compiler can consume.
namespace protobuf {
function message<T>(): string; // returns .proto schema text
}First example
import typia, { tags } from "typia";
interface User {
id: string;
age: number & tags.Type<"uint32">;
hobbies: string[];
}
const proto: string = typia.protobuf.message<User>();
console.log(proto);
//
// syntax = "proto3";
// message User {
// required string id = 1;
// required uint32 age = 2;
// repeated string hobbies = 3;
// }TypeScript Source
import typia, { tags } from "typia";
typia.protobuf.message<IMember>();
interface IMember {
id:
| (string & tags.Sequence<11>)
| (number & tags.Type<"uint64"> & tags.Sequence<12>)
| (Uint8Array & tags.Sequence<13>);
name: (string & tags.Sequence<20>) | null;
children: Array<IMember> & tags.Sequence<30>;
keywords: Map<string, string> & tags.Sequence<40>;
thumbnail:
| (string & tags.Format<"uri"> & tags.ContentMediaType<"image/*">)
| Uint8Array;
email: string & tags.Format<"email">;
hobbies: Array<IHobby>;
}
interface IHobby {
id: string & tags.Format<"uuid">;
name: string;
valid: boolean;
}Wire types
Protocol Buffer has narrower numeric types than TypeScript (int32, uint32, int64, uint64, float, double). Use type tags to pick the wire type explicitly:
type Score = number & tags.Type<"float">;
type Population = bigint & tags.Type<"uint64">;
type Mixed = (number & tags.Type<"int32">) | (bigint & tags.Type<"uint64">);If you donโt tag a number, typia treats it as double. If you donโt tag a bigint, typia treats it as int64.
The TypeScript compiler will block invalid combinations โ you canโt put
tags.Type<"uint32">on abigint, for example.
TypeScript Source
import typia, { tags } from "typia";
interface TypeTagExample {
// ATOMIC TYPES
int32: number & tags.Type<"int32">;
uint32: number & tags.Type<"uint32">;
uint64: bigint & tags.Type<"uint64">;
int64: number & tags.Type<"int64">;
float: number & tags.Type<"float">;
double: number | undefined;
string: string | null;
// UNION TYPES
uint32_or_double: number & (tags.Type<"uint32"> | tags.Type<"double">);
int32_or_uint64:
| (number & tags.Type<"int32">)
| (bigint & tags.Type<"uint64">);
int32_or_float_or_uint64:
| (number & (tags.Type<"int32"> | tags.Type<"float">))
| (bigint & tags.Type<"uint64">);
// ARRAY AND MAP
uint64_array: Array<bigint & tags.Type<"uint64">>;
int32_map?: Map<number & tags.Type<"int32">, string> | null;
}
//----
// PROTOBUF MESSAGE SCHEMA
//----
typia.protobuf.message<TypeTagExample>();
//----
// DECODE FUNCTION
//----
typia.protobuf.createDecode<TypeTagExample>();
//----
// ENCODE FUNCTION
//----
typia.protobuf.createEncode<TypeTagExample>();Comment tags
Same wire types are also expressible as @type comments:
interface User {
/** @type uint32 */
age: number;
}Same caveats as validators/tags: comment tags are not type-checked (a typo silently falls back to double), they only work on object properties, and they canโt express union numeric types. Prefer type tags unless youโre maintaining a JSDoc-style codebase.
TypeScript Source
import typia from "typia";
export interface CommentTagExample {
/** @type int32 */
int32: number;
/** @type uint32 */
uint32?: number | null;
/** @type uint64 */
uint64?: number;
/** @type int64 */
int64: number;
/** @type float */
float: number | null;
double: number;
string: string;
}
//----
// PROTOBUF MESSAGE SCHEMA
//----
typia.protobuf.message<CommentTagExample>();
//----
// DECODE FUNCTION
//----
typia.protobuf.createDecode<CommentTagExample>();
//----
// ENCODE FUNCTION
//----
typia.protobuf.createEncode<CommentTagExample>();Restrictions
Protocol Bufferโs type system is much narrower than TypeScriptโs. The following are not representable:
Top-level type must be a single static object
number, Array<T>, Cat | Dog, Record<string, T> โ none of these are valid top-level shapes. Wrap them in an object.
TypeScript Source
import typia from "typia";
interface Cat { type: "cat"; name: string; ribbon: boolean }
interface Dog { type: "dog"; name: string; hunt: boolean }
typia.protobuf.message<bigint>();
typia.protobuf.createDecode<Record<string, number>>();
typia.protobuf.createDecode<Map<number & typia.tags.Type<"float">, Dog>>();
typia.protobuf.createEncode<boolean[]>();
typia.protobuf.createEncode<Cat | Dog>();Container nesting is limited
Array<T>, Map<K, V>, and Record<string, T> are containers. The rules:
- No two-dimensional containers.
number[][]is not allowed. Neither isMap<string, number[]>. - No unions inside container values.
Map<string, Cat | Dog>is not allowed. Mapkeys must be atomic and non-union.Map<Cat, string>andMap<string | number, Dog>are not allowed.
TypeScript Source
import typia from "typia";
interface IPointer<T> { value: T }
interface Cat { type: "cat"; name: string; ribbon: boolean }
interface Dog { type: "dog"; name: string; hunt: boolean }
typia.protobuf.message<IPointer<number[][]>>();
typia.protobuf.createEncode<IPointer<Record<string, string[]>>>();
typia.protobuf.createDecode<IPointer<Map<string, Cat | Dog>>>();
typia.protobuf.message<IPointer<Map<Cat, string>>>();
typia.protobuf.message<IPointer<Map<number | string, Dog>>>();Types with no Protocol Buffer counterpart
These are rejected outright:
any/unknown(you donโt know how to encode an unknown value)- function types
Set<T>,WeakSet<T>,WeakMap<K, V>โ useArray<T>/Map<K, V>insteadDate,Boolean,BigInt,Number,Stringโ use the primitive (string,boolean, โฆ) instead- Typed arrays except
Uint8Arrayโ useUint8Arrayfor binary data
TypeScript Source
import typia from "typia";
interface Something {
any: any;
unknown: unknown;
closure: () => void;
dict: Set<string> | WeakSet<Something> | WeakMap<Something, string>;
date: Date;
classic: String;
buffer: ArrayBuffer;
}
typia.protobuf.message<Something>();Where to go next
- Encoding a value โ
protobuf.encode - Decoding bytes โ
protobuf.decode - Numeric constraints in tags โ Special Tags