Как заставить typescript интерпретировать параметр как пространство имен, а не класс, когда они имеют одинаковое имя — gRPC

#typescript #vue.js #grpc #protobuf.js

#typescript #vue.js #grpc #protobuf.js

Вопрос:

Извините за длинный заголовок…

Я реализую простую систему crud little с gRPC и typescript, моя проблема: Автоматически сгенерированный файл создает класс и тип для каждого параметра в my protoFile , например: параметр userId генерирует класс с getUserId и т. Д. И пространство имен с типом для userId .

Проблема в том, что когда я пытаюсь использовать метод в my client , typescript ожидает класс в качестве параметра, а не тип.

поэтому вместо getUsersById({id: 1}, callback) … он просит меня сделать getUsersById(new UserId) .

пользователь.Прото:

 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) — Определение функций

 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) — определение типов и классов:

 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,
  }
}
 

Клиент.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
});
 

параметр userId выдает следующую ошибку:

Аргумент типа ‘{id: number; }’ не может быть присвоен параметру типа ‘userId’. В типе ‘{id: number; }’ отсутствуют следующие свойства из типа ‘userId’: getId, setId, serializeBinary, ToObject и еще 8.Vetur(2345)

Мне кажется, что typescript делает вывод, что getUserById первый параметр имеет тип Class UserId , а не тип, исходящий из пространства UserId имен.

Могу ли я что-нибудь с этим поделать? Поскольку он был автоматически сгенерирован, я чувствую, что он должен правильно интерпретировать? Я что-то еще путаю? Я новичок в gRPC, поэтому, возможно, я делаю что-то не так.

Заранее спасибо!

Ответ №1:

Сообщение UserId генерируется как класс JavaScript. Вы должны предоставить экземпляр этого класса, альтернативы на самом деле нет, по крайней мере, с кодом, сгенерированным protocol-gen-grpc-web .

Есть несколько альтернативных генераторов кода для TypeScript, которые не требуют использования классов. ts-proto, а также protobuf-ts используют простые интерфейсы вместо классов для представления сообщений. (Я автор protobuf-ts).

Например, protobuf-ts генерирует код из вашего user.proto, который можно использовать следующим образом:

 // gRPC-web provided by @protobuf-ts/grpcweb-transport
const transport = new GrpcWebFetchTransport("http://localhost:5001");

const client = new UserServiceClient(transport);

const {response} = await client.getUserById({id: 123});

console.log(
    response.id,
    response.name,
    response.age
);
 

Я думаю, что ts-proto также получил поддержку grpc-web некоторое время назад. Может быть, попробуйте один из них и посмотрите, нравится ли вам сгенерированный код лучше.

Комментарии:

1. Вы правы, моя проблема в том, что я просматривал онлайн-примеры, в которых использовался gRPC на других платформах, а не gRPC web, если вы создаете прототипы с помощью gRPC web, вам нужно создать экземпляр своего «сообщения», а затем инициировать его значения через сеттеры и использовать их через геттеры… Большое спасибо!