microsoft / microsoft/TypeScript

Suggestion: Distributive code inference "as dist"

Offen
#62,631 3 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Awaiting More Feedback Suggestion
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
2 T. 4 Std.
Gemergte PRs (30 T.)
132

Beschreibung

🔍 Search Terms
  • distributive inference
  • distributed function
  • as dist
  • as distributed
✅ Viability Checklist
⭐ Suggestion

Add dist / distributed keyword to enable inference in a distributive manner.

Concept 1: Inline as dist

Handle the inference by distributing over all types used by the expression:

const transform = (val: [1, 1] | [2, 2]) => [val[0], val[1]] as dist;
typeof transform; // (val: [1, 1] | [2, 2]) => [1, 1] | [2, 2]

//

const add = (...[a, b]: [number, number] | [bigint, bigint]) => a + b as dist;
typeof add; // (...[a, b]: [number, number] | [bigint, bigint]) => number | bigint
Concept 2: Function-level dist return type

Allow distribution across an entire function body:

const f = <T extends [number, number] | [bigint, bigint]>(...[a, b]: T): dist => {
    const sum = a + b; // number | bigint
    const arr = [a, b]; // [number, number] | [bigint, bigint];
    return {
        sum,
        arr
    } // {sum: number, arr: [number, number]} | {sum: bigint, arr: [bigint, bigint]}
}

const res = f(3,7); // {sum: number, arr: [number, number]}
const rez = f(3n,7n); // {sum: bigint, arr: [bigint, bigint]}

typeof f; // (<T extends [number, number]>(...[a, b]: T) => { sum: number; arr: [number, number]; }) & (<T extends [bigint, bigint]>(...[a, b]: T) => { sum: bigint; arr: [bigint, bigint]; })

Concept 1 could also be supported on function expressions with equivalent behavior to Concept 2:

const f2 = (<T extends [number, number] | [bigint, bigint]>(...[a, b]: T) => {/*code*/}) as dist; // Equivalent to "f"
Notes

These are early-stage concepts meant to illustrate the core idea. The specific syntax and behaviors should be refined during implementation.

Open questions: Interaction with as const, generics, nested functions, opt-out mechanisms, etc. will need consideration.
Potential breaking change: Codebases with a type called dist would conflict, though this would go against TypeScript's PascalCase naming convention for types.

📃 Motivating Example

Currently, code is inferred in a non-distributive manner:

Example inspired by geon

const transform = (val: [1, 1] | [2, 2]) => [val[0], val[1]] as const;
// Inferred return type: readonly [1 | 2, 1 | 2]
// Expected return type: readonly [1, 1] | readonly [2, 2]

This can be addressed by duplicating your code logic within a distributive context in the typesystem:

const transform = <T extends [1, 1] | [2, 2]>(val: T) => [val[0], val[1]] as T extends unknown
    ? [T[0], T[1]]
    : never
;

const res = transform(undefined! as [1, 1] | [2, 2]); // [1, 1] | [2, 2]
const rez = transform([1, 1]); // [1, 1]

But this is very repetitive and awkward. Besides that, this only works if the automatic inference doesn't cause a conflict to begin with and can be simply translated into the type system:

Example inspired by Emilio Platzer and Mudloop

// @ts-expect-error: Operator '+' cannot be applied to types 'number | bigint' and 'number | bigint'.(2365)
const add = (...[a, b]: [number, number] | [bigint, bigint]) => a + b;
💻 Use Cases
  1. What do you want to use this for?
    Preserving precise type relationships when transforming union types, and enabling operations across union branches that are currently rejected by the type checker.
  2. What shortcomings exist with current approaches?
    Current workarounds require manually duplicating code logic in the type system using conditional types. This is verbose, error-prone, and fails when TypeScript rejects the runtime-aligned code itself
  3. What workarounds are you using in the meantime?
    Manually encoding distributive logic using conditional types

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

Es wurden keine Implementierungsdateien, Tests oder Compiler-Einstiegspunkte identifiziert. Beginne mit der Überprüfung der beiden vorgeschlagenen Syntaxvarianten, der motivierenden Beispiele und der offenen Fragen zu Generics, verschachtelten Funktionen und Opt-outs. Als abgeschlossen gilt die Aufgabe erst, wenn ein endgültiges Design feststeht und der Implementierungsumfang festgelegt werden kann.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
typescript
Bereich
compilers
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
20/100

Neue Issues direkt in Ihr Postfach

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