Tips on instructing TypeScript to view a parameter as a namespace instead of a class, especially when they share the same name - gRPC

Apologies for the lengthy title...

I am in the process of developing a basic crud system using gRPC and typescript. My issue lies in the fact that the auto-generated file creates a class and a type for each parameter in my protoFile. For example, the UserId parameter generates a class with getUserId, and a namespace with the type for UserId.

The problem arises when trying to use the method in my client, as typescript expects a class as a parameter instead of the type.

So instead of getUsersById({id: 1}, callback)... it prompts me to use 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 triggers 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 appears that typescript is interpreting that getUserById first parameter is of the type Class UserId rather than the type originating from the namespace UserId.

Is there a solution to this issue? Since it was auto-generated, shouldn't it interpret correctly? Could I be making a mistake elsewhere? I am relatively new to gRPC so I may have misunderstood something.

Thank you in advance!

Answer №1

When creating the message UserId, it is generated as a JavaScript class. To work with this class, you must provide an instance of it, as there is no real alternative provided by the code generated by protoc-gen-grpc-web.

However, there are alternative code generators available for TypeScript that do not rely on classes. Tools like ts-proto and protobuf-ts use simple interfaces instead of classes to represent messages. (I am the author of protobuf-ts).

For instance, using protobuf-ts, you can generate code from your user.proto file that can be utilized in the following way:

// Using 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
);

It is worth noting that ts-proto has also added grpc-web support recently. I recommend trying out one of these alternatives to see if you prefer the generated code.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The act of exporting an enum from a user-defined TypeScript path leads to the error message "Module not

I have set up a custom path as explained in this particular discussion. "baseUrl": ".", "paths": { "@library/*": [ "./src/myFolder/*" ], } Within this module, I am exporting an Enum. export enum EN ...

I attempted to create a checkbox cell within a datatable using Vuetify, unfortunately, it is not functioning as intended

Is there a way to create a checkbox within a datatable so that it automatically checks if the user is an admin in the system? I attempted to write some code but it's not functioning correctly. While {{value==1}} does work and accurately determines tr ...

Using Vue.js to filter a list based on index matches

I need help with synchronizing two lists. The first list is displayed in a carousel format, and the second list contains details corresponding to each card in the carousel. I have successfully implemented logic to track the current index of the displayed c ...

Using Vue.js to conditionally render data in a v-for loop

Below is the code snippet I am working with: <article class="project-card" v-for="item in en.projects" Additionally, here are some import statements: import fr from '../assets/datas/fr.json' import en from '../assets/datas/en. ...

Exploring the canDeactivateFn syntax with Angular Documentation

As a first-year university student, I recently discovered that the canDeactivate() guard in Angular is deprecated, while the canDeactivateFn guard functions without any issues. Admittedly, navigating through official documentation is still new to me. From ...

The 'hide' property is not recognized on this type during the build process

Just starting out with the quasar framework. I've created a new component and used it in a modal popup. I followed the steps outlined here. The dialog opens using the method below. methods: { openStoreModal(store:Store) { this.$q.dialog({ ...

The disappearance of UI elements in Angular 13 and Bootstrap 5 when new routes are introduced

After spending a considerable amount of time on website development, I have hit a roadblock with the navigation. Whenever I set up a route, the entire user interface disappears and refuses to load. I have searched extensively but found no solution to this ...

What is causing the CSS transition to fail in the updated DOM?

When attempting to apply a CSS transition as shown in the following code snippet: https://jsfiddle.net/sunyuu/e58sfeva/17/ var Main = { data () { return { isActive: false, childStyle: {} }; }, methods: { sho ...

Using an individual object instead of an array to create an autocomplete form

I am currently grappling with Vue JS as I embark on building my first Auto complete component using VueCLI. Below is the snippet of code that works fine when using an array: https://pastebin.com/a8AL8MkD filterStates() { this.filteredStates = this.stat ...

Implementing Vue's dynamic component addition feature

I recently came across an interesting article on dynamically adding different components in Vue. The article explains a good method for binding different components to tabs, but I have a specific requirement. I want to bind one type/name component that wil ...

I'm confused as to why my server is displaying all columns from my database, even though I specifically indicated which columns I

Why is my b-table showing all columns from my table even though I only selected some? Here is the server-side code that fetches the selected columns. It seems to work fine with just bootstrap and not with bootstrap-vue. router.get('/users', fun ...

Ensuring the accuracy of forms using third-party verification services

While working on an Angular form validation using an external service, I encountered a cannot read property of undefined error. The component contains a simple form setup: this.myForm = this.fb.group({ username: ['', [this.validator.username] ...

TypeScript's HashSet Implementation

I'm working on a simple TypeScript task where I need to extract unique strings from a map, as discussed in this post. Here's the code snippet I'm using: let myData = new Array<string>(); for (let myObj of this.getAllData()) { let ...

This phrase cannot be invoked

My code seems correct for functionality, but I am encountering an error in my component that I do not know how to resolve. Can someone please help me with this issue? This expression is not callable. Not all constituents of type 'string | ((sectionNa ...

Incorporate a new method into a TypeScript class from a separate file

I'm curious about the feasibility of adding additional functions to a class prototype in Typescript. Here's my dilemma: I have a file containing a class, for example image.ts: export class Image { b64img: string; } Since this class is gene ...

What is the best way to retrieve information from a JavaScript file?

Learning.vue <template> <div> <button @click="test()">test</button> </div> </template> <script> import records from './records.js' export default { data () { return { ...

An error was encountered in compiler.js at line 1021, stating that an unexpected value 'UserService' was imported by the module 'UserModule'. It is recommended to add a @NgModule annotation to resolve this issue

As a PHP programmer new to Angular, I am facing an issue while trying to retrieve user properties from a Laravel API. When attempting this, I encountered the following error: compiler.js:1021 Uncaught Error: Unexpected value 'UserService' importe ...

The potential object null may lead to an absence of the 'value' property in the type 'EventTarget'

I am facing a problem that I am unable to resolve. The error in my HTML reads: Object is possibly 'null' and Property 'value' does not exist on type 'EventTarget'. HTML <select [(ngModel)]="selectedProvincia" (ch ...

Assigning dynamic key value pairs in Angular 4 using Typescript

I'm currently attempting to construct an object using keys that are specified in an environment file, meaning the keys would vary depending on the environment. import { environment } from '../environments/environment' export abstract class ...

Error: Attempting to access properties of an undefined value (specifically 'and') while utilizing an observable

My profilecomponent is designed to receive user data from a service in the form of an Object public profiles$: Observable<IPerson>; constructor(private router: Router, private userService: UserService) {} ngOnInit(): void { this.profiles$ ...