Sorry for the long title...
I am implementing a simple crud little system with gRPC and typescript, my problem is: The auto generated file creates a class and a type for each parameter in my protoFile
, for instance: UserId parameter generates a class with getUserId etc, and a namespace with the type for UserId.
The problem is that when I try to use the method in my client
, typescript expects a class as a parameter and not the type.
so instead of getUsersById({id: 1}, callback)
... it asks me to do getUsersById(new UserId)
.
user.Proto:
syntax = "proto3";
package userServicePKG;
message User {
int32 id = 1;
string name = 2;
int32 age = 3;
}
message UserId {
int32 id = 1;
}
service UserService{
rpc getUserById (UserId) returns (User);
}
UserServiceClientPb.ts (Protobuf generated) - Functions definition
methodInfogetUserById = new grpcWeb.AbstractClientBase.MethodInfo(
User,
(request: UserId) => {
return request.serializeBinary();
},
User.deserializeBinary
);
getUserById(
request: UserId,
metadata: grpcWeb.Metadata | null): Promise<User>;
getUserById(
request: UserId,
metadata: grpcWeb.Metadata | null,
callback: (err: grpcWeb.Error,
response: User) => void): grpcWeb.ClientReadableStream<User>;
getUserById(
request: UserId,
metadata: grpcWeb.Metadata | null,
callback?: (err: grpcWeb.Error,
response: User) => void) {
if (callback !== undefined) {
return this.client_.rpcCall(
new URL('/userServicePKG.UserService/getUserById', this.hostname_).toString(),
request,
metadata || {},
this.methodInfogetUserById,
callback);
}
return this.client_.unaryCall(
this.hostname_ +
'/userServicePKG.UserService/getUserById',
request,
metadata || {},
this.methodInfogetUserById);
}
user_pb.d.ts (Protobuf Generated) - Define types and classes:
export class UserId extends jspb.Message {
getId(): number;
setId(value: number): UserId;
serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): UserId.AsObject;
static toObject(includeInstance: boolean, msg: UserId): UserId.AsObject;
static serializeBinaryToWriter(message: UserId, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): UserId;
static deserializeBinaryFromReader(message: UserId, reader: jspb.BinaryReader): UserId;
}
export namespace UserId {
export type AsObject = {
id: number,
}
}
Client.Vue:
const client = new UserServiceClient('http://localhost:5001', null, null);
let userId = { id:1 };
client.getUserById(userId, function (error: grpcWeb.Error, response: any) {
//do something
});
the parameter userId fires the following error:
Argument of type '{ id: number; }' is not assignable to parameter of type 'UserId'. Type '{ id: number; }' is missing the following properties from type 'UserId': getId, setId, serializeBinary, toObject, and 8 more.Vetur(2345)
It looks to me that typescript is inferring that getUserById
first parameter is of the type Class UserId
instead of the type coming from the namespace UserId
.
Is there something I can do about it? Since it was autogenerated, I feel like it should interpret correctly? Am I messing up something else? I am new to gRPC so I may be doing something wrong.
Thanks in advance!
The message
UserId
is generated as a JavaScript class. You have to provide an instance of this class, there isn't really any alternative, at least not with the code generated by protoc-gen-grpc-web.There are some alternative code generators for TypeScript-based that do not require you to use classes. ts-proto as well as protobuf-ts use simple interfaces instead of classes to represent messages. (I am the author of protobuf-ts).
For example, protobuf-ts generates code from your user.proto that can be used like this:
I think ts-proto also got grpc-web support some time ago. Maybe give one of those a try and see if you like the generated code better.