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(grpc-js): Add support for impl type to server.addService #1556
Conversation
|
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.
The difference there was just an oversight. I'm fine with making this change.
UntypedHandleCall | ||
> = UntypedServiceImplementation | ||
>( | ||
service: ServiceDefinition<ImplementationType>, |
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 think this might need to be Partial<ServiceDefinition<ImplementationType>>
. A service definition is allowed to be missing some methods.
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.
Don't think Partial<T>
is needed here as ServiceDefinition<T>
is defined in such a way that allows the service definition to be missing some methods?
export interface UntypedServiceImplementation {
[name: string]: UntypedHandleCall;
}
export type ServiceDefinition<
ImplementationType = UntypedServiceImplementation
> = {
readonly [index in keyof ImplementationType]: MethodDefinition<any, any>;
};
But I may be wrong... 🤔
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.
Oh, right, the problem might actually be the other way around: if the implementation object has methods that aren't part of the service definition, those keys will be considered missing in the service definition.
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.
And I think the Partial<T>
there solves that problem: the inferred ServiceDefinition<ImplementationType>
type may have extra keys corresponding to implementation object methods that are not part of the service, and partial allows those to be omitted from the service definition object.
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.
Will action shortly – just bogged down with other work; sorry!
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.
FYI – faced some issues related to microsoft/TypeScript#15300 with the original implementation. |
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 strongly prefer to avoid type assertions wherever possible, because they effectively invalidate the compiler's type checks.
packages/grpc-js/src/server.ts
Outdated
implementation: UntypedServiceImplementation | ||
addService< | ||
ImplementationType extends { | ||
readonly [index in keyof ImplementationType]: UntypedHandleCall; |
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.
Does this mean that every member of ImplementationType
is an UntypedHandleCall
? If so, that is not accurate. Implementation objects are allowed to have other members that are not registered as gRPC method handlers.
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 changed this just now.
packages/grpc-js/src/server.ts
Outdated
@@ -161,7 +165,7 @@ export class Server { | |||
throw new Error('addService() requires two objects as arguments'); | |||
} | |||
|
|||
const serviceKeys = Object.keys(service); | |||
const serviceKeys = Object.keys(service) as Array<keyof ImplementationType>; |
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.
This type assertion is not exactly accurate. The service can have keys pointing to method descriptions that are not implemented in the implementation object.
And it's not clear that this provides a significant benefit in terms of narrowing the type, because another type assertion name as string
is needed later, and that would be implicit without this type assertion here.
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.
Reverted.
packages/grpc-js/src/server.ts
Outdated
@@ -189,13 +193,13 @@ export class Server { | |||
let impl; | |||
|
|||
if (implFn === undefined && typeof attrs.originalName === 'string') { | |||
implFn = implementation[attrs.originalName]; | |||
implFn = implementation[attrs.originalName as keyof ImplementationType]; |
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.
Similar to the previous point, this method may not be implemented in the implementation object, so this is not necessarily an accurate type assertion.
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.
Reverted.
packages/grpc-js/src/server.ts
Outdated
} | ||
|
||
if (implFn !== undefined) { | ||
impl = implFn.bind(implementation); | ||
impl = implFn.bind(implementation as 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.
Why is this cast needed at all? It looks to me like bind
accepts an unknown
, so anything should work.
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.
Reverted.
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.
Thank you for your contribution.
I'm sorry, but I'm going to have to revert this. It broke my code in a different PR, and that probably means that it has the potential to break other people's code. |
Thanks for letting me know, I looked at the PR and the error, I do believe #1561 will fix that. |
Adding support for
ImplementationType
toserver.addService(...)
.It seems like the original (non-js)
grpc
type definitions support this - https://github.com/grpc/grpc-node/blob/grpc%401.24.x/packages/grpc-native-core/index.d.ts#L266.Not sure if there was a reason not to do it in grpc-js?
Thanks!