-
Notifications
You must be signed in to change notification settings - Fork 337
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: oneof=union breaks wrapper types #458 #462
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.
Few minor questions but overall looks great, thanks @boukeversteegh !
@@ -78,7 +78,9 @@ export const BatchQueryRequest = { | |||
|
|||
fromJSON(object: any): BatchQueryRequest { | |||
const message = createBaseBatchQueryRequest(); | |||
message.ids = (object.ids ?? []).map((e: any) => String(e)); | |||
if (Array.isArray(object?.ids)) { |
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 like the isSet
helper method, but for the arrays, @webmaster128 had just done quite a bit of work to rework all of our assignments to be one-liners, with the idea of a) minimizing code size, and b) eventually turning these into object literals, like:
return {
a: ...,
b: ....,
c: ...,
}
Which in theory should (with some handwaving) help v8 optimiziations.
Can we keep the previous ?? []
approach?
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 see, so the idea is to get rid of the createBase...()
call?
It made me wonder already why the default values for each field were overwritten each time. Thanks for clarifying, now it makes more sense.
I'll change back to the ternary style.
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 see, so the idea is to get rid of the createBase...() call
Yep! @aikoven has an in-flight PR that is either doing this soon, or at least moving us closer to that I think.
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.
@stephenh Ah I didn't know @aikoven was already working on this. So I actually just made a PR #463 to do what you described. It was kind of simple as I was already deep into this part of the code, and had a solution in mind for the trickier 'oneof' case.
So, @aikoven, if you also had something in the works and want to finish that instead, please close my PR. Sorry if I got in the way.
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 for adapting. Yeah the overall direction is to move away from mutability where you never know for sure what is set when. So a lot of recent work sent into exactly one assignment per field.
This PR nicely adds to that journey
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 see, so the idea is to get rid of the createBase...() call?
We can easily get rid of it in fromJSON and fromPartial. For decode
it will probably still be required as we are reading the serialized data into the object in unknown order and not all fields are serialized.
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.
Yeah, for decode
I was thinking that calling createBase()
to get an object literal with every field assigned (right now I don't think it does literally every field, just some primitives), and then have the decode
for loop just assign over existing keys, would be most likely to trigger the supposedly / I'm-admittedly-cargo-culting-a-little-bit optimization of "create objects with all keys set and then don't add/remove keys". :-)
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.
right now I don't think it does literally every field, just some primitives
That changed in #457, see e.g. #457 (comment). Now all fields are set to default and decode can override them.
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.
Ah great!
); | ||
|
||
const isSet = conditionalOutput( | ||
'isSet', |
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.
Nice
chunks.push(code`unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined { | ||
if (ctx.options.oneof === OneofOption.UNIONS) { | ||
chunks.push(code`unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined { | ||
if (message.kind?.$case === 'nullValue') { |
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.
Ah sure, this makes sense...
Fwiw I was looking for a struct.ts
that had this code in it, but don't see it ... looks like you did change the oneof-union test case to use a struct, but maybe the new google/protobuf/struct.ts
just didn't get checked in as part of the PR?
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.
Great catch! Wow :-) Just pushed it.
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.
Looks great, thanks @boukeversteegh !
Thank you for your review and coordination! |
🎉 This PR is included in version 1.97.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
@@ -81,3 +80,7 @@ if (util.Long !== Long) { | |||
util.Long = Long as any; | |||
configure(); | |||
} | |||
|
|||
function isSet(value: any): boolean { | |||
return value !== null && value !== undefined; |
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.
Now that this is pulled out into a nicely named helper, you can even use value != null
.
Do we need type narrowing? You can do something like
function isSet<X>(value: X | undefined | null): value is X {
return value != null;
}
function isObject(value: any): boolean { | ||
return typeof value === 'object' && value !== null; | ||
} |
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.
👍
Just in case type narrowing matters at some point, have a look at this implementation I wrote for a different project: https://github.com/cosmos/cosmjs/blob/v0.27.0-rc2/packages/utils/src/typechecks.ts#L1-L12
Tests and fixes the problem that the
unwrap
functions need to consideroneof=union
option. See #458 .oneof=unions
enabled.fieldName
to read properties from the passed object, instead of thejsonName
. fixed.isObject
andisSet
)During implementation, I found out that
protobuf.js
doesn't unwrap Value objects, so it becomes incompatible.Originally, I wanted to solve this with an extra option to ts-proto, that 'unwraps' Value objects within fromJSON, but it made things complicated and broke other things, so I decided to keep things simple for now and just work around the issue in the test itself.