microsoft / microsoft/TypeScript

Spread operator fails to distribute over union when recursive type call is inlined instead of aliased

Offen
#62,812 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bug Domain: Conditional Types Help Wanted
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
2 T. 4 Std.
Gemergte PRs (30 T.)
132

Beschreibung

🔎 Search Terms

distribution, spread

🕗 Version & Regression Information

This is the behavior in every version I tried from version 4.1.5 upwards to 5.9.3 and in nightly v6.0.0-dev.20251127

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.3#code/C4TwDgpgBAwgTgewM5IAqICYFcDGwA8AqgHYCWCxANLAlscBHFBAB4PEZJR0DWxCAd2IBtALoA+KAF4AUFHk06DJq3acow0sQBmjKAC1GCagDozW3UwBKEJMFFyFAfigAKEuWLM2EDlwt6ALIQALYARoyOCs4aweGMpmbwyGiYuAQeFNQ2duIO0dEAXFDEEABujACUUVDFYgDcMgA2EMBQDHYAjMXJKOgI2Hj4xFjxTAA+UHZwWgDm1MJ0GBDaWhAYEvXyAPTbGtNzolCTwiNjDqCQUAAiEGDAABad0rCIfWlDZxETU8AzxPMNEsVmsNpJdvs-odjhovowHC02h1gAAmYquOE-A4A7xqfw6PSZLwudxkCi43zqXj8IRQFzCImJEy3e5PUTFUoVOCVWolcpVLYQ4TYwEio4nTHUTHiyH-QHSmGnUbfahi5qtdq2YAAZnRmJhIopfigASYRLpbnNqkpXGpgmJGkZUDMJl6qQG6WGyr0kxFC2Bq1KYPZfK5PI5-O5Wx2e2FUIBqvjsxlSrGUu9cARGuRABY9RmDUmjepTVBzSSrT5jXbafSnS7XHG5SnpZUQ5yqryO1GoEK-b9m4rJSUMyn+wqJRnE82ZEA

💻 Code
type CrossProduct<Union, Counter extends unknown[]> =
    Counter extends [infer Zero, ...infer Rest]
    ? (Union extends infer Member
        ? [Member, ...CrossProduct<Union, Rest>]
        : never)
    : [];
let test1: CrossProduct<number | string, [undefined]>;  // [string] | [number]
type Depth1 = CrossProduct<number | string, [undefined]> // [string] | [number]
let test2: (number | string extends infer Union ? (Union extends unknown ? [Union, ...Depth1]: never) : never); // [string, string] | [number, number] | [string, number] | [number, string]
let test3: (number | string extends infer Union ? (Union extends unknown ? [Union, ...CrossProduct<number | string, [undefined]>]: never) : never);   // [string, string] | [number, number]
let test4: (number | string extends infer Union ? (Union extends unknown ? [Union, ...([string] | [number])]: never) : never); // [string, string] | [number, number] | [string, number] | [number, string]
🙁 Actual behavior

The types of test2 and test4 evaluate to the correct, expected, fully distributed type, while the type of test3 is incompletely distributed (as displayed by IntelliSense in the Playground).

The only difference between test2 and test3 is that in test3 the type that is aliased as Depth1 in test2 is inlined in test3. Thus, both should evaluate to the same type. The type of test2 is correct.

test4 also evaluates correctly. Here the alias is replaced by the literal evaluation result.

🙂 Expected behavior

The type of variable test3 should evaluate to

[string, string] | [number, number] | [string, number] | [number, string]

But it evaluates to

[string, string] | [number, number]

Additional information about the issue

This problem matters since the recursive call in test3 is what I actually need for the construction of a respective recursive type.

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne mit der verlinkten TypeScript Playground-Reproduktion und vergleiche die ausgewerteten Typen von test2, test3 und test4. Verfolge den rekursiven CrossProduct-Aufruf und die Spread-Behandlung im type checker; abgeschlossen ist die Aufgabe, wenn test3 als dieselbe vollständig verteilte Union wie test2 und test4 ausgewertet wird.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
compilers
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
38/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.