agreatfool / grpc_tools_node_protoc_ts

Generate TypeScript d.ts definitions for generated js files from grpc_tools_node_protoc
MIT License
498 stars 56 forks source link

error TS2345: Argument of type `'ISchoolService'` is not assignable to parameter of type `'ServiceDefinition<UntypedServiceImplementation>'` #144

Open khteh opened 3 months ago

khteh commented 3 months ago

I generate grpc Typescript code with:

"proto": "npx grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./grpc/client --grpc_out=./grpc/client --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` -I ./grpc/proto ./grpc/proto/*.proto && npx grpc_tools_node_protoc --plugin=protoc-gen-ts=`which protoc-gen-ts` --ts_out=./grpc/client -I ./grpc/proto ./grpc/proto/*.proto",

and hit the following error:

Services/SchoolService.ts:205:23 - error TS2345: Argument of type 'ISchoolService' is not assignable to parameter of type 'ServiceDefinition<UntypedServiceImplementation>'.
  Index signature for type 'string' is missing in type 'ISchoolService'.

205     server.addService(SchoolService, new ServerImpl());

Environment

rmlakhani01 commented 2 months ago

I got a similar error when I was building mine. I was able to resolve the issue by adding the following line to the service.ts file. I've attached my code for more clarity.

server.ts

import { Server, ServerCredentials } from '@grpc/grpc-js'
import { GreeterServiceService } from './proto/greet_grpc_pb'
import { GreeterService } from './service'

const main = () => {
  const server = new Server()
  server.bindAsync(
    '0.0.0.0:50051',
    ServerCredentials.createInsecure(),
    (err: Error | null, port: number) => {
      if (err) console.error('Error caught: ' + err)

      server.addService(GreeterServiceService, new GreeterService())
      console.log(`gRPC Server listening on port ${port}`)
    },
  )
}

main()

service.ts

import { handleUnaryCall, UntypedHandleCall } from '@grpc/grpc-js'
import { IGreeterServiceServer } from './proto/greet_grpc_pb'
import { GreetRequest, GreetResponse } from './proto/greet_pb'

export class GreeterService implements IGreeterServiceServer {
  [name: string]: UntypedHandleCall \\ <---THE FIX IS HERE 
  greet: handleUnaryCall<GreetRequest, GreetResponse> = (call, callback) => {
    const request = call.request
    const response = new GreetResponse()
    response.setResult(
      'Hello, ' + request.getName() + '! Welcome to gRPC World :)',
    )
    callback(null, response)
  }
}

I hope this helps!

khteh commented 2 months ago

Doesn't help.

rmlakhani01 commented 2 months ago

Would you mind sharing the code?

khteh commented 2 months ago

https://github.com/khteh/Node.JSRestAPI

rmlakhani01 commented 2 months ago

I can't find the file where the error occurred. Could you link it?

khteh commented 2 months ago

grpc branch. https://github.com/khteh/Node.JSRestAPI/blob/grpc/src/webapi/Services/SchoolService.ts

rmlakhani01 commented 2 months ago

So I poked around and I think the compilation of your decorators might be part of the problem as an example this is my compiled file for my greeter service.

greet_grpc_pb.d.ts

// package: greeter
// file: greet.proto

/* tslint:disable */
/* eslint-disable */

import * as grpc from "@grpc/grpc-js";
import * as greet_pb from "./greet_pb";

interface IGreeterServiceService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
    greet: IGreeterServiceService_IGreet;
}

interface IGreeterServiceService_IGreet extends grpc.MethodDefinition<greet_pb.GreetRequest, greet_pb.GreetResponse> {
    path: "/greeter.GreeterService/Greet";
    requestStream: false;
    responseStream: false;
    requestSerialize: grpc.serialize<greet_pb.GreetRequest>;
    requestDeserialize: grpc.deserialize<greet_pb.GreetRequest>;
    responseSerialize: grpc.serialize<greet_pb.GreetResponse>;
    responseDeserialize: grpc.deserialize<greet_pb.GreetResponse>;
}

export const GreeterServiceService: IGreeterServiceService;

// HERE
export interface IGreeterServiceServer extends grpc.UntypedServiceImplementation {
    greet: grpc.handleUnaryCall<greet_pb.GreetRequest, greet_pb.GreetResponse>;
}

export interface IGreeterServiceClient {
    greet(request: greet_pb.GreetRequest, callback: (error: grpc.ServiceError | null, response: greet_pb.GreetResponse) => void): grpc.ClientUnaryCall;
    greet(request: greet_pb.GreetRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: greet_pb.GreetResponse) => void): grpc.ClientUnaryCall;
    greet(request: greet_pb.GreetRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: greet_pb.GreetResponse) => void): grpc.ClientUnaryCall;
}

export class GreeterServiceClient extends grpc.Client implements IGreeterServiceClient {
    constructor(address: string, credentials: grpc.ChannelCredentials, options?: Partial<grpc.ClientOptions>);
    public greet(request: greet_pb.GreetRequest, callback: (error: grpc.ServiceError | null, response: greet_pb.GreetResponse) => void): grpc.ClientUnaryCall;
    public greet(request: greet_pb.GreetRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: greet_pb.GreetResponse) => void): grpc.ClientUnaryCall;
    public greet(request: greet_pb.GreetRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: greet_pb.GreetResponse) => void): grpc.ClientUnaryCall;
}

The interface that you have declare in the file is missing the inheritance of grpc.UntypedServiceImplementation

This is the commands I used to generate my files.

./scripts/gen.sh

npm install grpc_tools_node_protoc_ts --save-dev

# generate js codes via grpc-tools
grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:./proto \
--grpc_out=grpc_js:./proto \
--plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` \
-I ./proto \
./proto/*.proto

# generate d.ts codes
protoc \
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--ts_out=grpc_js:./proto \
-I ./proto \
./proto/*.proto

Hopefully this helps :) Just an FYI I am just getting more familiar with writing TypeScript gRPC code.

khteh commented 2 months ago

Your build script seems to work but when I run the application:

$ n run start

> node.jsrestapi@1.0.0 start
> export NODE_ENV=development&& node build/src/webapi/server.js

file:///usr/src/Node.JSRestAPI/build/src/webapi/Services/SchoolService.js:10
import { CommonStudentsResponse } from "../grpc/client/school_pb.js";
         ^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: The requested module '../grpc/client/school_pb.js' does not provide an export named 'CommonStudentsResponse'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:171:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:254:5)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:485:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:109:5)

Node.js v22.6.0
rmlakhani01 commented 2 months ago

you need to use a package called ts-node