microsoft / microsoft/TypeScript
Suggestion: Distributive code inference "as dist"
Nessuno ha ancora preso questa issue.
- Lingua principale
- Go
- Stelle
- 111k
- Fork
- 14.3k
- Merge medio
- 2g 4h
- PR unite (30g)
- 132
Descrizione
🔍 Search Terms
- distributive inference
- distributed function
- as dist
- as distributed
✅ Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
⭐ 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
- 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. - 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 - What workarounds are you using in the meantime?
Manually encoding distributive logic using conditional types
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Non sono stati identificati file di implementazione, test o punti di ingresso del compilatore. Inizia esaminando le due sintassi proposte, gli esempi motivanti e le questioni aperte relative ai generics, alle funzioni annidate e agli opt-out. Per considerare conclusa l’attività sarebbe necessario definire il design prima di poter delimitare l’implementazione.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- typescript
- Ambito
- compilers
- Tipo di issue
- Funzionalità
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Ferma
- Chiarezza
- Da chiarire
- Idoneità per principianti
- 20/100