message()
function
export namespace protobuf {
export function message<T>(): string;
}
typia.protobuf.message()
function returns a Protocol Buffer message (structure) as a string value.
With this message()
function, you can share *.proto
files with other languages. If you want to customize byte order or define specific type (that is not supported in the TypeScript) like uint32
, use comment tags by following comment tags section.
import typia, { tags } from "typia";
interface ICustomer {
id: number & tags.Type<"int32">;
email: string & tags.Format<"email">;
name: string;
pet: null | ICat | IDog;
memo: null | Map<string, string>;
logins: Array<ICustomerLogin>;
}
interface ICat {
type: "cat";
name: string;
ribbon: boolean;
}
interface IDog {
type: "dog";
name: string;
hunt: boolean;
}
interface ICustomerLogin {
success: boolean;
href: string & tags.Format<"url">;
referrer: string & tags.Format<"url">;
ip: string & (tags.Format<"ipv4"> | tags.Format<"ipv6">);
time: string & tags.Format<"date-time">;
}
typia.protobuf.message<ICustomer>();
ICustomer.protosyntax = "proto3"; message ICustomer { required int32 id = 1; required string email = 2; required string name = 3; oneof pet { ICat v4 = 4; IDog v5 = 5; } map<string, string> memo = 6; repeated ICustomerLogin logins = 7; } message ICat { required string type = 1; required string name = 2; required bool ribbon = 3; } message IDog { required string type = 1; required string name = 2; required bool hunt = 3; } message ICustomerLogin { required bool success = 1; required string href = 2; required string referrer = 3; required string ip = 4; required string time = 5; }
Type Tags
By using type tags, you can use special numeric types that are not supported in the TypeScript.
Just import Type
(or typia.tags.Type
) type, and combine it with number
or bigint
type through intersection symbol number & typia.tagsType<"float">
case. If you want to declare an union numeric type, combine |
and bracket (()
) symbols properly like below.
When you take a mistake that choosing different target type, TypeScript compiler would block it with compliation error message. Therefore, have a confidence when using the Type
tag. For such type safety reason, I recommend to use Type
tag instead of using comment tags as much as possible.
number & (Type<"uint32"> | Type<"double">)
number
type can be bothuint32
anddouble
(number & Type<"int32">) | (bigint & Type<"uint64">)
number
isint32
bigint
isuint64
(number & (Type<"int32">)| Type<"float">) | (bigint & Type<"uint64">)
number
can be bothint32
andfloat
bigint
isuint64
import typia, { tags } from "typia";
export 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 & Type<"uint64">>;
int32_map?: Map<number & 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
By using @type {target}
comment tag, you also can use special numeric types.
However, this way is not recommended, because it can't perform union numeric types, and cannot be used in Array
and Map
types. When you declare @type int32
statement, target number
type be fixed as int32
type, and never can have another numeric type by declaring union statements.
Also, those comment tags are not type safe. If you take a mistake when writing a comment tag, it will not be detected by the compiler, and will cause an error at runtime. For example, if you write a mis-spelled keyword like @type unit32
, the target number
type would be double
type, and you can identify it just by running the program (or visiting playground website).
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
You know what? Expression power of Protocol Buffer is extremely narrower than type system of TypeScript. For example, Protocol Buffer can't express complicate union type containing array. Also, Protocol Buffer can't express multi dimensional array type, either.
In such reason, when converting TypeScript type to Protocol buffer message schema, lots of restrictions are exist. Let's study which types of TyeScript are not supported in Protocol Buffer. For reference, if you try to call typia.protobuf.message<T>()
function with unsupported type, typia
will generate compile errors like below example cases.
At first, top level type must be a sole and static object.
If you try to use number
or Array<T>
type as a top level type, typia
will generate compile error like below. Dynamic object types like Record<string, T>
, or Map<string, T>
types are not allowed either. For reference, the sole object means that, union of object types is not allowed, either.
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>();
At next, in Protocol Buffer, those types are categorized as container types.
Array<T>
Map<Key, T>
Record<string, T>
(dynamic object)
Also, those container types does not allow over two-dimensional stacking. Therefore, it is not possible to declaring two dimensional array like number[][]
, or Array
type in Map
like Map<string, number[]>
. Besides, value type of those container also do not support union type either.
Additionally, about Map<Key, T>
type, key type must be an atomic type. It means that, only boolean
, number
, bigint
and string
types are allowed. Also, key type cannot be union type, either.
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>>>();
At last, those types are all not allowed.
any
functional type
Set<T>
,WeakSet<T>
andWeakMap<T>
Date
,Boolean
,BigInt
,Number
,String
- Binary classes except
Uint8Array
Uint8ClampedArray
,Uint16Array
,Uint32Array
,BigUint64Array
Int8Array
,Int16Array
,Int32Array
,BigInt64Array
ArrayBuffer
,SharedArrayBuffer
andDataView
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>();