protobuf-ts
protobuf-ts copied to clipboard
Proto2: nested types are always optional, required is ignored
Hi! I am working on a system with uses proto2, using protobuf-ts on the web browser side and having an issue where nested types are always generated as optional. Here is an example:
message Test {
required User user = 1;
repeated Role roles = 2;
required string name = 3;
optional Settings settings = 4;
}
export interface Test {
/**
* @generated from protobuf field: users.User user = 1;
*/
user?: User;
/**
* @generated from protobuf field: repeated users.Role roles = 2;
*/
roles: Role[];
/**
* @generated from protobuf field: string name = 3;
*/
name: string;
/**
* @generated from protobuf field: optional users.Settings settings = 4;
*/
settings?: Settings;
}
user field shouldn't be optional in that case, only settings. As you see name is generated correctly. I know support for proto2 is limited but I hope for some help here, it is really, really hard to find a good library for TypeScript with full proto2 support. Protobuf-ts is the closest one from multiple libs I tested... Thanks in advance for any help!
This issue is not limited to Proto2
. I'm seeing it with Proto3
also:
syntax = "proto3";
message Org {
int32 id = 1;
string name = 2;
}
message User {
int32 id = 1;
string email = 2;
string name = 3;
Org org = 4;
}
/**
* @generated from protobuf message Org
*/
export interface Org {
/**
* @generated from protobuf field: int32 id = 1;
*/
id: number;
/**
* @generated from protobuf field: string name = 2;
*/
name: string;
}
/**
* @generated from protobuf message User
*/
export interface User {
/**
* @generated from protobuf field: int32 id = 1;
*/
id: number;
/**
* @generated from protobuf field: string email = 2;
*/
email: string;
/**
* @generated from protobuf field: string name = 3;
*/
name: string;
/**
* @generated from protobuf field: Org org = 4;
*/
org?: Org;
}
I'm using the following command:
npx protoc \
--ts_out "dist/" \
--ts_opt server_generic,client_generic,optimize_code_size \
--proto_path "src/" \
"./src/Test.proto"
In proto3, message fields are always optional on the wire. That means somebody can send you a User
message without the org
field, and it's perfectly valid. The org
property is optional to reflect that. I don't know why optional
is allowed for message fields in proto3, it doesn't make sense to me and I guess it was an oversight.
Oh interesting... We've been using improbable-eng/ts-protoc-gen
for the last year and totally didn't notice this. Seems like a really odd decision in Proto3
especially with the explicit option of optional
available. Weird.
I just double checked that lib's output and it's the same there, for reference, I used:
npx protoc \
--plugin "protoc-gen-ts=.\\node_modules\\.bin\\protoc-gen-ts.cmd" \
--ts_out "dist/" \
--proto_path "src/" \
"./src/Test.proto"
export namespace Org {
export type AsObject = {
id: number,
name: string,
}
}
export namespace User {
export type AsObject = {
id: number,
email: string,
name: string,
org?: Org.AsObject,
}
}
Seems kind of odd, because I'd presume that a Service responding with a User
would always include user.org
(because its not explicitly defined as optional) but even if it does, Typescript will always have to do user.org?.id
which opens up the door to some unnecessary confusion.
Thanks for the info. I'll bear this in mind for the future.
@timostamm can I ask why the close? The original questions is not fixed, regrading proto2, not proto3.
I missed the original description, let's keep this open!
I have the same issue. To get rid of confusion like user.org?.id
(where we know that user.org is always defined) I was forced to build an additional layer of types where I make the optional fields required: For example:
export type UserAsObject = Required<User.AsObject>
This is quite a poor workaround, is there any option to make proto generate these objects as not optional?