|
| 1 | +//// [tests/cases/conformance/types/spread/spreadTupleUnionDistribution.ts] //// |
| 2 | + |
| 3 | +=== spreadTupleUnionDistribution.ts === |
| 4 | +// Repro from #62812 |
| 5 | +// Spread operator fails to distribute over union when recursive type call is inlined instead of aliased |
| 6 | + |
| 7 | +type CrossProduct<Union, Counter extends unknown[]> = |
| 8 | +>CrossProduct : Symbol(CrossProduct, Decl(spreadTupleUnionDistribution.ts, 0, 0)) |
| 9 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 3, 18)) |
| 10 | +>Counter : Symbol(Counter, Decl(spreadTupleUnionDistribution.ts, 3, 24)) |
| 11 | + |
| 12 | + Counter extends [infer Zero, ...infer Rest] |
| 13 | +>Counter : Symbol(Counter, Decl(spreadTupleUnionDistribution.ts, 3, 24)) |
| 14 | +>Zero : Symbol(Zero, Decl(spreadTupleUnionDistribution.ts, 4, 26)) |
| 15 | +>Rest : Symbol(Rest, Decl(spreadTupleUnionDistribution.ts, 4, 41)) |
| 16 | + |
| 17 | + ? (Union extends infer Member |
| 18 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 3, 18)) |
| 19 | +>Member : Symbol(Member, Decl(spreadTupleUnionDistribution.ts, 5, 26)) |
| 20 | + |
| 21 | + ? [Member, ...CrossProduct<Union, Rest>] |
| 22 | +>Member : Symbol(Member, Decl(spreadTupleUnionDistribution.ts, 5, 26)) |
| 23 | +>CrossProduct : Symbol(CrossProduct, Decl(spreadTupleUnionDistribution.ts, 0, 0)) |
| 24 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 3, 18)) |
| 25 | +>Rest : Symbol(Rest, Decl(spreadTupleUnionDistribution.ts, 4, 41)) |
| 26 | + |
| 27 | + : never) |
| 28 | + : []; |
| 29 | + |
| 30 | +// Basic test - this works |
| 31 | +let test1: CrossProduct<number | string, [undefined]>; // [string] | [number] |
| 32 | +>test1 : Symbol(test1, Decl(spreadTupleUnionDistribution.ts, 11, 3)) |
| 33 | +>CrossProduct : Symbol(CrossProduct, Decl(spreadTupleUnionDistribution.ts, 0, 0)) |
| 34 | + |
| 35 | +type Depth1 = CrossProduct<number | string, [undefined]> // [string] | [number] |
| 36 | +>Depth1 : Symbol(Depth1, Decl(spreadTupleUnionDistribution.ts, 11, 54)) |
| 37 | +>CrossProduct : Symbol(CrossProduct, Decl(spreadTupleUnionDistribution.ts, 0, 0)) |
| 38 | + |
| 39 | +// With alias - this should work and give full cross product |
| 40 | +let test2: (number | string extends infer Union ? (Union extends unknown ? [Union, ...Depth1]: never) : never); |
| 41 | +>test2 : Symbol(test2, Decl(spreadTupleUnionDistribution.ts, 15, 3)) |
| 42 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 15, 41)) |
| 43 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 15, 41)) |
| 44 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 15, 41)) |
| 45 | +>Depth1 : Symbol(Depth1, Decl(spreadTupleUnionDistribution.ts, 11, 54)) |
| 46 | + |
| 47 | +// Expected: [string, string] | [number, number] | [string, number] | [number, string] |
| 48 | + |
| 49 | +// With inlined type - this should also work but currently doesn't distribute properly |
| 50 | +let test3: (number | string extends infer Union ? (Union extends unknown ? [Union, ...CrossProduct<number | string, [undefined]>]: never) : never); |
| 51 | +>test3 : Symbol(test3, Decl(spreadTupleUnionDistribution.ts, 19, 3)) |
| 52 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 19, 41)) |
| 53 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 19, 41)) |
| 54 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 19, 41)) |
| 55 | +>CrossProduct : Symbol(CrossProduct, Decl(spreadTupleUnionDistribution.ts, 0, 0)) |
| 56 | + |
| 57 | +// Expected: [string, string] | [number, number] | [string, number] | [number, string] |
| 58 | +// Actual (bug): [string, string] | [number, number] |
| 59 | + |
| 60 | +// With literal union - this works |
| 61 | +let test4: (number | string extends infer Union ? (Union extends unknown ? [Union, ...([string] | [number])]: never) : never); |
| 62 | +>test4 : Symbol(test4, Decl(spreadTupleUnionDistribution.ts, 24, 3)) |
| 63 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 24, 41)) |
| 64 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 24, 41)) |
| 65 | +>Union : Symbol(Union, Decl(spreadTupleUnionDistribution.ts, 24, 41)) |
| 66 | + |
| 67 | +// Expected: [string, string] | [number, number] | [string, number] | [number, string] |
| 68 | + |
| 69 | +// Test that the types are actually correct by checking assignability |
| 70 | +type Expected = [string, string] | [number, number] | [string, number] | [number, string]; |
| 71 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 72 | + |
| 73 | +// These should all be true (no error) |
| 74 | +type Test1Check = Expected extends typeof test2 ? true : false; |
| 75 | +>Test1Check : Symbol(Test1Check, Decl(spreadTupleUnionDistribution.ts, 28, 90)) |
| 76 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 77 | +>test2 : Symbol(test2, Decl(spreadTupleUnionDistribution.ts, 15, 3)) |
| 78 | + |
| 79 | +type Test2Check = typeof test2 extends Expected ? true : false; |
| 80 | +>Test2Check : Symbol(Test2Check, Decl(spreadTupleUnionDistribution.ts, 31, 63)) |
| 81 | +>test2 : Symbol(test2, Decl(spreadTupleUnionDistribution.ts, 15, 3)) |
| 82 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 83 | + |
| 84 | +// If the bug is fixed, these will also be true (no error) |
| 85 | +type Test3Check = Expected extends typeof test3 ? true : false; |
| 86 | +>Test3Check : Symbol(Test3Check, Decl(spreadTupleUnionDistribution.ts, 32, 63)) |
| 87 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 88 | +>test3 : Symbol(test3, Decl(spreadTupleUnionDistribution.ts, 19, 3)) |
| 89 | + |
| 90 | +type Test4Check = typeof test3 extends Expected ? true : false; |
| 91 | +>Test4Check : Symbol(Test4Check, Decl(spreadTupleUnionDistribution.ts, 35, 63)) |
| 92 | +>test3 : Symbol(test3, Decl(spreadTupleUnionDistribution.ts, 19, 3)) |
| 93 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 94 | + |
| 95 | +type Test5Check = Expected extends typeof test4 ? true : false; |
| 96 | +>Test5Check : Symbol(Test5Check, Decl(spreadTupleUnionDistribution.ts, 36, 63)) |
| 97 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 98 | +>test4 : Symbol(test4, Decl(spreadTupleUnionDistribution.ts, 24, 3)) |
| 99 | + |
| 100 | +type Test6Check = typeof test4 extends Expected ? true : false; |
| 101 | +>Test6Check : Symbol(Test6Check, Decl(spreadTupleUnionDistribution.ts, 38, 63)) |
| 102 | +>test4 : Symbol(test4, Decl(spreadTupleUnionDistribution.ts, 24, 3)) |
| 103 | +>Expected : Symbol(Expected, Decl(spreadTupleUnionDistribution.ts, 24, 126)) |
| 104 | + |
| 105 | +// Force an error if checks fail |
| 106 | +const _check1: Test1Check = true; |
| 107 | +>_check1 : Symbol(_check1, Decl(spreadTupleUnionDistribution.ts, 42, 5)) |
| 108 | +>Test1Check : Symbol(Test1Check, Decl(spreadTupleUnionDistribution.ts, 28, 90)) |
| 109 | + |
| 110 | +const _check2: Test2Check = true; |
| 111 | +>_check2 : Symbol(_check2, Decl(spreadTupleUnionDistribution.ts, 43, 5)) |
| 112 | +>Test2Check : Symbol(Test2Check, Decl(spreadTupleUnionDistribution.ts, 31, 63)) |
| 113 | + |
| 114 | +const _check3: Test3Check = true; // This will error if bug exists |
| 115 | +>_check3 : Symbol(_check3, Decl(spreadTupleUnionDistribution.ts, 44, 5)) |
| 116 | +>Test3Check : Symbol(Test3Check, Decl(spreadTupleUnionDistribution.ts, 32, 63)) |
| 117 | + |
| 118 | +const _check4: Test4Check = true; // This will error if bug exists |
| 119 | +>_check4 : Symbol(_check4, Decl(spreadTupleUnionDistribution.ts, 45, 5)) |
| 120 | +>Test4Check : Symbol(Test4Check, Decl(spreadTupleUnionDistribution.ts, 35, 63)) |
| 121 | + |
| 122 | +const _check5: Test5Check = true; |
| 123 | +>_check5 : Symbol(_check5, Decl(spreadTupleUnionDistribution.ts, 46, 5)) |
| 124 | +>Test5Check : Symbol(Test5Check, Decl(spreadTupleUnionDistribution.ts, 36, 63)) |
| 125 | + |
| 126 | +const _check6: Test6Check = true; |
| 127 | +>_check6 : Symbol(_check6, Decl(spreadTupleUnionDistribution.ts, 47, 5)) |
| 128 | +>Test6Check : Symbol(Test6Check, Decl(spreadTupleUnionDistribution.ts, 38, 63)) |
| 129 | + |
0 commit comments