Skip to content

fix: support null in DeepPartial when useNullAsOptional #1187

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

LuckyEden
Copy link

Added null to the Builtin union when useNullAsOptional=true, so that DeepPartial<T> correctly preserves null values.

Copy link
Owner

@stephenh stephenh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lmk if you're fine adding the test; happy to merge this either way. Thanks!

@@ -688,7 +688,7 @@ function makeDeepPartial(options: Options, longs: ReturnType<typeof makeLongUtil
"Builtin",
code`type Builtin = Date | Function | Uint8Array | string | number | boolean |${
options.forceLong === LongOption.BIGINT ? " bigint |" : ""
} undefined;`,
} ${options.useNullAsOptional === true ? "null |" : ""} undefined;`,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think of this approach (adding null to Builtin), but it's passing all the tests, so sgtm! :-D

We can merge this as-is, but fwiw it might be a good idea to create a use-null-as-optional.test.ts file, next to the use-null-as-optional.ts file and do something like:

const u = User.createPartial({
  // ideally this was a compile error w/o your change, but now works
  profile: null,
});
expect(u.profile).someAssertion();

Just to make sure we don't accidentally regress this change in the future.

Happy to merge this as-is, but just will be some caveats that I could see someone thinking "null shouldn't be in Builtin" down the road, and not realize they're breaking your use case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. I will add testing to make sure it is covered. I am also thinking of making it parametric to cover a few variations more easily. Does that make sense? Or will just test be enough? Maybe I can test the ORM compatibility more?

Copy link
Author

@LuckyEden LuckyEden May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a question, if the value is null in a generate operation with the useNullAsOptional option, shouldn't it be considered null?

The default is undefined;

message.profile = (object.profile !== undefined && object.profile !== null)
      ? ProfileInfo.fromPartial(object.profile)
      : undefined;

Maybe it can be done with other options, I need to review them again. Sorry if it sounds ridiculous. :D

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @LuckyEden , sorry for the late reply here; yeah, I think you're right...

Historically ts-proto leaned towards my own bias of "always use undefined" and so would convert null and undefined to "just undefined" for simplicity.

Primarily b/c on the wire, protobuf doesn't have a way to different "what is null" vs. "what is undefined" so I didn't think it made sense for the programmer to have to write code that was like "if undefined do this, if null do that" when really only one of those ifs would ever run.

However with useNullAsOptional, I'd have to refresh my memory on what that opt is for 😅 but it seems reasonable for that : undefined to be : null...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants