pick-deep: Fix the case when the key or element is an union type (#1224)#1241
pick-deep: Fix the case when the key or element is an union type (#1224)#1241taiyakihitotsu wants to merge 18 commits intosindresorhus:mainfrom
pick-deep: Fix the case when the key or element is an union type (#1224)#1241Conversation
|
@taiyakihitotsu Thanks for the PR! I’m a bit occupied with festivities, I’ll try to review it sometime next week. |
|
I've added a fix commit for #1223. @sindresorhus @som-sm |
|
I found a bug in this case and will fix it as well. type unionKeyUnionObject_Actual = PickDeep<{obj: {a: string; b: number; c: boolean} | {d: 0}}, `obj.${'b' | 'c' | 'd'}`>; // {obj: {b: number; d: 0; c: boolean}} |
|
And type-fest/source/union-to-tuple.d.ts Line 13 in ee29ef7 I'll move it to internal if ok to combine them into one PR. |
|
I’ve got an idea for the fix, but not sure when I can get back to it, so I leave this as a draft for now. |
|
I fixed the issues (#1224, #1223) and a bug in the comment I posted earlier. diffI defined a converter that transforms template literals like (When the implementation of Path relies on Union type distribution, the original structure can be lost. For example, in an extreme case like I've also defined some internal helper functions for certain getter functions, which ignore the distinction between
IsNever bugOutdated. see this move to internalIt seems reasonable to mark some of the functions as internal. |
Is there a simpler reproduction of where |
|
This comment is outdated, see this new comment It may only fail when using recursion with Current
|
|
type LastOfUnion<T> =
UnionToIntersection<
T extends infer A
? () => A
: never> extends () => (infer R)
? R extends T ? R : never
: never;
This works well only in the test cases above, but it cannot replace the existing code. |
type LastOfUnion<T> =
UnionToIntersection<
T extends infer A
? () => A
: never> extends () => (infer R)
? R
: never;
type a = LastOfUnion<never>; // unknownOK I see that this returns The current (at 590efae8223ed1ac61855840dff114360b7c6807) definition of type test = LastOfUnion<Exclude<string, string>> extends infer U ? PreviousIsNever<U> : 'false';
// ^ false
export type PreviousIsNever<T> = [T] extends [never] ? true : false;Apologies for my misunderstanding. In any case, Should I include it in this PR? Or not? |
In this PR, yes. |
|
Thanks for your response! I wrote a comment and provided two examples of how to use it for recursion. And I made a small change to The latest status of this PR
|
9a66b14 to
cab8667
Compare
|
Sorry for leaving this hanging.
The functionality hasn’t changed since #1241 (comment). I just wanted to share the update. |
|
I think this issue (#880) is related as well. type unionElement1_Actual = PickDeep<{obj: string | {a: string; b: number; c: {d: 'result'}} | null | undefined}, 'obj.b'>;
type unionElement1_Expected = {obj: string | null | undefined | {b: number}};
expectType<true>({} as IsEqual<unionElement1_Actual, unionElement1_Expected>); |
|
Thanks for your review! my noteThis test case which you suggested had not been passed at first (I pasted it). // Test: readonly array should preserve readonly modifier when picking into elements
type ReadonlyArrayContainer = {
items: ReadonlyArray<{a: 1; b: 2}>;
};
type readonlyArrayPick_Actual = PickDeep<ReadonlyArrayContainer, `items.${number}.a`>;
type readonlyArrayPick_Expected = {items: ReadonlyArray<{a: 1}>};
expectType<true>({} as IsEqual<readonlyArrayPick_Actual, readonlyArrayPick_Expected>);This review's code (#1241 (comment)) fixed this tests (#1241 (comment)): This PR closes #1224 (and #1223 tested in https://github.com/sindresorhus/type-fest/pull/1241/files#diff-3104c46023d65f634fe4bbb58d8a42358c369d3967ca7f29c62eea3c94388910R195). To clarify this point, I’ve added a comment and a test case (in this commit fdcb3df) to ensure that |
|
I've fixed The other status has not been changed from #1241 (comment). |
|
Drafting until #1349 is merged |
This PR fixes #1224.
(I’m opening this because I previously sent a message about the issue, but it seems that no one has started working on it.)
Current Status
#1241 (comment) -> #1241 (comment) -> #1241 (comment)
Approach
The issue can be broken down into two separate bugs:
PickDeepfails to pick values when the object’s properties are of a union type. (2025/09/29 ... ok)PickDeepdoesn't properly merge results when the path is a union type.(2025/09/29 ... it has a bug)(2025/10/01 ... ok)The first was fixed by applying distribution over the union type in the object.
For the second issue, the fix involves evaluating each path in the union individually, obtaining the result as a union of objects, and then merging them using(2025/10/01 ... See)MergeNarrow.related issue
#1224, #1223, #880 (See)
I'm not sure whether the expected value shown in #1223 is truly correct according to the(2026/01/16 ... See)PickDeepspecification, so I’ve decided to leave that issue for now.(In the current commit, the result for that case would be(2025/10/01 ... See){arr: []}. I think this is caused byMergeNarrow.)If I should include a fix for #1223 in this PR as well, please let me know.(2026/01/16 ... See)note
I rewrote the tests to be more strict, using the following pattern:
This is because some previous tests using
expectType<T>(value)were passing unintentionally.