-
Notifications
You must be signed in to change notification settings - Fork 56
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
Allow serializing identical messages #289
Conversation
packages/protobuf/src/message.ts
Outdated
export function isMessage<T extends Message<T>>(value: any): value is T { | ||
return ( | ||
!!value && | ||
typeof value.equals === "function" && | ||
typeof value.clone === "function" && | ||
typeof value.fromBinary === "function" && | ||
typeof value.fromJson === "function" && | ||
typeof value.fromJsonString === "function" && | ||
typeof value.toBinary === "function" && | ||
typeof value.toJson === "function" && | ||
typeof value.toJsonString === "function" && | ||
typeof value.getType === "function" | ||
); | ||
} |
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.
Hmm... I don't think this is the ideal approach to this. I'd suggest to instead implement a generic ttype guard on the MessageType instead and, in there, only loosely check that the given Mesasge has a getType function and that it returns the same message type (typeName comparison)
is(message: unknown): message is Message<ThisMessageType> {
// Check that `message` has a `getType` function and that that returns
// a `MessageType` with `typeName === this.typeName`
}
if (isMessage<T>(value) && value.getType().typeName === type.typeName) { | ||
return value; | ||
} |
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.
... So here, you'd instead do:
if (type.is(value)) {
return value;
}
Note: Not sure if the name is
is a good choice, but you get the idea.
I am not a maintainer though... So :-)
I just ran into this too while trying to stub out a slim Message object in a test. Generally I agree that I think it's good enough to check whether there's a |
@fubhy Thanks for checking my PR. Great suggestions! I changed accordingly. I came with "conforms" as a method name. However I need some help because I had to change the generator code as well, but I don't know how to regenerate the well known protobuf files and now everything is broken due to the missing methods there. |
Yeah that's a bit of a chicken-and-egg problem... The Not sure how the buf.build team handles that locally but I simply revert all local changes to @timostamm @smaye81 I think it would be good to configure I've configured my plugin repo similarly FYI. |
Ok, it seems I fixed all the issues. All tests pass and no there are no lint errors or warnings. The main changes are in the following files: In
In
In
In the
I think during the next release the minor version might need to be bumped due to the API change, but I guess that is not part of this PR. Edit: @fubhy thanks for the suggestions! |
I force re pushed all commits because I had the wrong git user.name and user.email set. |
Sandor, thank you for looking into this! In the issue you put up, we take the wrong code path if In other cases where The best way forward here is to fix the code paths that need to be more robust, and not add a type guard on |
@timostamm Are you against adding such a type guard on the generated code altogether or just for the sake of solving the original issue? I'd definitely see use for this type guard (see example below): const conforming = new Example();
const nonConforming = new OtherType();
Example.conforms(conforming); // true
Example.conforms(nonConforming); // false @rideg Apologies for leading you down the wrong path here! |
I'm against a type guard in the generated code because it will only help in a very narrow band of use cases, where you have a class, but it might not have the identity you expect. For cases like this, it is also possible to override Object.defineProperty(SomeMessage, Symbol.hasInstance, {
value(instance: unknown) {
return instance instanceof Message && instance.getType().typeName == SomeMessage.typeName;
},
}); To be truly useful, fields must also be compared, otherwise it would give a false positive for a message with a removed field, for example. So far, I don't believe we need this complexity though. We can simply make our code paths a bit smarter to solve the original issue. Apologies to both of you for not chiming in earlier. |
There is another kind of type guard that would work well in combination with the recursive function isMessage<T extends Message<T>>(arg: unknown, type: MessageType<T>): arg is Plain<T>; This could be a welcome addition for folks who want to deconstruct and construct messages without caring about classes. But this is also something best added as a standalone function instead of a method on By the way, protobuf-ts has them - see the manual. They were added because it only generates interfaces, not classes, and there is no equivalent to |
Ok I see where you are coming from. Thanks for clarifying! |
Sure, thanks for the responses, reviews. I am going to close this PR as it is not necessary anymore. |
This PR fixes the issues explained in issue/283 by checking the if the incoming value is a Message and its typeName equals to the field descriptor's typeName.