-
Notifications
You must be signed in to change notification settings - Fork 770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix missing TypeScript return type for serverStreaming
calls.
#1167
Fix missing TypeScript return type for serverStreaming
calls.
#1167
Conversation
In the generated typescript code this will ensure that the generic parameters will be infered from the values passed to the constructor
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lukasmoellerch Thanks so much for the fix! This is pretty smart :D Should be a great improvement! 😃
Just a few thoughts:
-
I think part of the problem is that the generated client stub (
EchoServiceClientPb.ts
in your example) doesn't specify return type. I wonder if that can be fixed as well?These are the relevant lines if i read correctly:
grpc-web/javascript/net/grpc/web/generator/grpc_generator.cc
Lines 618 to 622 in 1efe741
printer->Print(vars, "$js_method_name$(\n"); printer->Indent(); printer->Print(vars, "request: $input_type$,\n" "metadata?: grpcWeb.Metadata) {\n"); -
I wonder if you could just remove type specifications in our TS example to help verify that type reference indeed works?
grpc-web/net/grpc/gateway/examples/echo/ts-example/client.ts
Lines 112 to 124 in 1efe741
this.stream.on('data', (response: ServerStreamingEchoResponse) => { EchoApp.addRightMessage(response.getMessage()); }); this.stream.on('status', (status: grpcWeb.Status) => { if (status.metadata) { console.log('Received metadata'); console.log(status.metadata); } }); this.stream.on('error', (err: grpcWeb.RpcError) => { EchoApp.addRightMessage( 'Error code: ' + err.code + ' "' + err.message + '"'); }); FYI, you have to comment option 1 and uncomment option 2 to get Typescript input:
grpc-web/net/grpc/gateway/examples/echo/ts-example/client.ts
Lines 26 to 27 in 1efe741
// Option 2: import_style=typescript // import {EchoServiceClient} from './EchoServiceClientPb';
Thanks! :)
packages/grpc-web/index.d.ts
Outdated
requestSerializeFn: (request: REQ) => Uint8Array, | ||
responseDeserializeFn: (bytes: Uint8Array) => RESP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you make the return type to match the Closure type definition here? The fact it's currently using Uint8Arry
is implementation detail and should probably be omitted.
* @param {function(REQUEST): ?} requestSerializeFn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not quite sure why it would make sense to have these typed as wither unknown
or any
- if we allow more values here consumers will be allowed to create MethodDescriptor
instances which break internal invariants, e.g. I could create a MethodDescriptor
with a serialize function which returns a number and typescript won't complain, but during runtime it will break. In my opinion MethodDescriptor
is part of the public interface and thus the constructor should only allow valid values to be passed to the constructor. Nevertheless I can totally understand if you want it to return an interface / unknown instead, maybe it's just a matter of personal preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we allow more values here consumers will be allowed to create MethodDescriptor instances which break internal invariants
I get your sentiment in general.. Although because MethodDescriptor code is generated rather than specified by callers, this probably doesn't make a practical difference..
Nevertheless I can totally understand if you want it to return an interface / unknown instead, maybe it's just a matter of personal preference.
The main reason i want to keep this in sync with the Closure type annotation is that internally (Google) we're referring to the Closure type as source of truth, and only on Github we're also adding the TS type info. And because the internal types are subject to change, I don't want us to commit to stronger types in TS than we do in JS. If that makes sense :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok sure, thanks for the explanation. I reverted the types of requestSerializeFn
and responseDeserializeFn
to any
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot :)
packages/grpc-web/index.d.ts
Outdated
getRequestSerializeFn(): (request: REQ) => Uint8Array; | ||
getResponseDeserializeFn(): (bytes: Uint8Array) => RESP; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above :)
Thanks for the quick response! Regarding your first point:
We could add a return type declaration here, but the inferred return type is already correct I think: Because
Yes, I think we should be able to remove those. I'll quickly try that, thanks for the hint. |
Ok, I did some quick testing: After convincing typescript to use the new type declarations with the following tsconfig file: {
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"strict": true,
"allowJs": true,
"outDir": "./dist",
"types": ["node"],
"baseUrl": "./",
"paths": {
"grpc-web": ["../../../../../../packages/grpc-web/"]
}
},
"include": [
"client.ts",
"echo_pb.js",
"echo_pb.d.ts",
"echo_grpc_web_pb.js",
"echo_grpc_web_pb.d.ts",
"EchoServiceClientPb.ts"
],
"exclude": ["node_modules"]
} I indeed get the correct parameter type inferred and the parameter type inferred correctly: and the EchoServiceClient.serverStreamingEcho(request: ServerStreamingEchoRequest, metadata?: grpcWeb.Metadata | undefined): grpcWeb.ClientReadableStream<ServerStreamingEchoResponse> whereas previously I got the following error:
in the line where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lukasmoellerch Thanks so much for the change again! Mostly LGTM after resolving the open comments :)
We could add a return type declaration here, but the inferred return type is already correct I think: Because
serverStreaming
returns aClientReadableStream<RESP>
as per its declaration inindex.d.ts
the inferred return type will also beClientReadableStream
with the generic parameter being inferred from the method descriptor. We could still add a direct annotation there, but I don't think that it will change the type-checking in any way.
Yup agreed that inference is already working after your change.. I just thought also adding it to the generated TS file will add even a stronger guarantee of type info (e.g. inference might be accidentally broken).
But I'm fine if we we leave this as is and maybe do a follow-up change later :)
I indeed get the correct parameter type inferred and the parameter type inferred correctly:
Fantastic! Thanks for verifying! :)
packages/grpc-web/index.d.ts
Outdated
requestSerializeFn: (request: REQ) => Uint8Array, | ||
responseDeserializeFn: (bytes: Uint8Array) => RESP); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we allow more values here consumers will be allowed to create MethodDescriptor instances which break internal invariants
I get your sentiment in general.. Although because MethodDescriptor code is generated rather than specified by callers, this probably doesn't make a practical difference..
Nevertheless I can totally understand if you want it to return an interface / unknown instead, maybe it's just a matter of personal preference.
The main reason i want to keep this in sync with the Closure type annotation is that internally (Google) we're referring to the Closure type as source of truth, and only on Github we're also adding the TS type info. And because the internal types are subject to change, I don't want us to commit to stronger types in TS than we do in JS. If that makes sense :)
- Adds attribute type declaratiosn to method descriptor - Adds return type declaration to server streaming call
…ellerch/grpc-web into ts-method-descriptor-constructor
Sure, I modified the code generator, it should now generate the type annotations for server streaming methods and also for the generic params of the method descriptors. I hope the latter change is okay. |
Thanks a lot for the change! My personal preference is to omit the method descriptor types just because 1) users should rarely need to reference them directly, 2) this is a case where type inference becomes very clear after your change (whereas for the streaming endpoint it's more roundabout) :) But since this is generated code so a little extra verbosity maybe doesn't hurt.. I'll leave it to you :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lukasmoellerch Thanks again for this great change! And your patience during code review! LGTM now! :)
On second thought I have to agree: I think keeping the generated code clean is probably preferable here. I reverted that change. |
Oh cool sgtm! Thanks again for your contribution and patience through the reviews! :) I'll cut a 1.3.1 release soon. I'm excited for this improvement! :D (btw i've modified the name of the PR to better reflect the actual function of it :)) |
serverStreaming
calls.
@sampajano Do you plan you cut a new release so this can be pushed out? |
Yes! I'm sorry about the delay! I'll aim to do this in the next week! 😃 |
@chrisdrobison This should now be in release 1.3.1. thanks :) |
Should fix #950
In the generated typescript code this will ensure that the generic parameters will be inferred from the values passed to the constructor. This should only make a difference when the
import_style
setting is set totypescript
. Afterwards in the following code snippet the the response parameter will correctly infer toServerStreamingEchoResponse
, whereas previously it would be typed asunknown
.I am not entirely sure where tests for the
typescript
mode are located, I was only able to finde thetsc_tests
, but they seem to be using the dts generator.