Skip to content

Add UnwrapPartial type#1296

Merged
sindresorhus merged 3 commits intosindresorhus:mainfrom
porada:feature/unwrap-partial
Dec 25, 2025
Merged

Add UnwrapPartial type#1296
sindresorhus merged 3 commits intosindresorhus:mainfrom
porada:feature/unwrap-partial

Conversation

@porada
Copy link
Contributor

@porada porada commented Nov 14, 2025

Closes #1294.

@porada
Copy link
Contributor Author

porada commented Nov 14, 2025

Questions that popped up to my mind while working on this:

  1. Should we be more strict about array types? Technically UnwrapPartial<string[]> will pass as string[], but it doesn’t make much sense (neither does defining Partial<string[]>). We could reject types that aren’t based on plain objects, but are we keeping the promise of unwrapping then?
  2. I feel like I overdid the tests. @som-sm @sindresorhus — would any of you mind taking a look before we proceed with this?

@porada porada mentioned this pull request Nov 14, 2025
1 task
@porada porada force-pushed the feature/unwrap-partial branch from e97c402 to de00d90 Compare November 14, 2025 20:49
@porada
Copy link
Contributor Author

porada commented Nov 14, 2025

Aside from the points mentioned above, this PR is ready for review.

Once these changes are in, I’ll be happy to follow up with another Unwrap type.

@porada porada marked this pull request as ready for review November 14, 2025 20:57
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like the test cases could be simplified, there are so many test cases of the form UnwrapPartial<Partial<Something>>, with different values for Something. I think we don't need that many because the value of Something doesn't really matter, so we could just keep a few and remove the others.

Tests that don't have Partial are useful, like UnwrapPartial<{a?: string; b: number}>, UnwrapPartial<{a?: string; b?: number}>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey Som, thanks for the review.

I use UnwrapPartial<Partial<Something>> to ensure that the original Something type gets preserved. And there is a good number of types to consider.

Cases like UnwrapPartial<{a?: string; b: number}> and UnwrapPartial<{a?: string; b?: number}> are already covered (under `UnwrapPartial` preserves optional properties).

I’ll add any missing cases you pointed out, but feel free to prune things on your end as well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Cases like UnwrapPartial<{a?: string; b: number}> and UnwrapPartial<{a?: string; b?: number}> are already covered (under `UnwrapPartial` preserves optional properties).

Yeah, I meant all of those are useful, don't remove them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@som-sm — Unless you want to make any other specific changes, I think this PR is ready to merge.

Comment on lines +29 to +31
: never
)
: never;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should these be never or PartialObjectType, what would be better?

Copy link
Contributor Author

@porada porada Dec 1, 2025

Choose a reason for hiding this comment

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

The way I see this, you shouldn’t unwrap a modifier on a type that doesn’t have one in the first place (goes for all Unwrap* types). UnwrapPartial<number> should never resolve to number.

I was also wondering earlier:

Should we be more strict about array types? Technically UnwrapPartial<string[]> will pass as string[], but it doesn’t make much sense (neither does defining Partial<string[]>).

After thinking it over, I’m leaning towards not giving arrays any special treatment. Let’s assume the user knows what they’re doing.

Copy link
Collaborator

Choose a reason for hiding this comment

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

After thinking it over, I’m leaning towards not giving arrays any special treatment. Let’s assume the user knows what they’re doing.

Yeah, that's fine.

The way I see this, you shouldn’t unwrap a modifier on a type that doesn’t have one in the first place (goes for all Unwrap* types). UnwrapPartial<number> should never resolve to number.

I brought this up because with unions, IMO, it feels better if UnwrapPartial<Partial<{x: number, y: number}> | [number, number]>> returned {x: number, y: number} | [number, number] as opposed to just {x: number; y: number}. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A union with a partial isn’t technically a partial, and handling special cases is a can of worms we probably don’t want to open 🙂

Copy link
Collaborator

Choose a reason for hiding this comment

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

A union with a partial isn’t technically a partial

No, I did not mean that Partial<{x: number, y: number}> | [number, number] is a partial. I meant that instantiating UnwrapPartial with a union that contains both partial and non-partial items would cause the non-partial items to be removed, which might not be desirable.

handling special cases is a can of worms we probably don’t want to open

No, we'd obviously not handle unions separately, I meant replacing never with PartialObjectType. I think that'd be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Writable (technically UnwrapReadonly) works that way already, so I’m fine with returning the original type for consistency. Just made the necessary edits.

:shipit:

Copy link
Collaborator

@som-sm som-sm Dec 24, 2025

Choose a reason for hiding this comment

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

Writable (technically UnwrapReadonly)

They're not the same, right? Just like Required and UnwrapPartial:

type Config = Partial<{port: number; host: string; secure?: boolean}>;
//=> {port?: number; host?: string; secure?: boolean}

type T1 = UnwrapPartial<Config>;
//=> {port: number; host: string; secure?: boolean}

type T2 = Required<T1>;
//=> {port: number; host: string; secure: boolean}

so I’m fine with returning the original type for consistency. Just made the necessary edits.

👍

@porada porada requested a review from som-sm December 1, 2025 13:43
@porada porada force-pushed the feature/unwrap-partial branch from 6c84802 to 839f6b3 Compare December 3, 2025 20:14
Copy link
Collaborator

@som-sm som-sm left a comment

Choose a reason for hiding this comment

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

PR LGTM! Apologies for the delay.

@porada
Copy link
Contributor Author

porada commented Dec 25, 2025

All good. Thanks for the passing review!

@som-sm @sindresorhus — Are you open to having a conversation about adding more Unwrap types in? I’d be happy to create a new issue for this.

@sindresorhus sindresorhus merged commit 99b0b07 into sindresorhus:main Dec 25, 2025
9 checks passed
@porada porada deleted the feature/unwrap-partial branch December 25, 2025 22:33
@som-sm
Copy link
Collaborator

som-sm commented Dec 26, 2025

@som-sm @sindresorhus — Are you open to having a conversation about adding more Unwrap types in? I’d be happy to create a new issue for this.

Yeah sure! And feel free to open PRs for other Unwrap types.

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.

Consider UnwrapPartial<T> as a way to revert the Partial<T> modifier

3 participants