microsoft / microsoft/TypeScript
Handle union type of void and Promise<void> in return types
Nessuno ha ancora preso questa issue.
- Lingua principale
- Go
- Stelle
- 111k
- Fork
- 14.4k
- Merge medio
- 1g 19h
- PR unite (30g)
- 117
Descrizione
Suggestion
🔍 Search Terms
void, async, second-order functions
✅ Viability Checklist
My suggestion meets these guidelines:
- 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 feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion (and motivating example)
(I apologize if there is already discussion around this; I haven't been able to find it.)
It is sometimes convenient for a second-order function to receive a function that may or may not be asynchronous:
function mySecondOrderFn(firstOrderFn: () => void | Promise<void>) {
return async () => {
console.log("Calling firstOrderFn()")
const output = await firstOrderFn()
console.log("Finished calling firstOrderFn()")
}
}
// @ts-expect-error I'd like to be able to do this:
mySecondOrderFn(() => 2)
// @ts-expect-error Or this, for that matter:
mySecondOrderFn(async () => 2)
Unfortunately () => void | Promise<void> seems to cancel TS's normal type inference around () => void:
const x: () => void = () => 2
// @ts-expect-error Type 'number' is not assignable to type 'void | Promise<void>
const y: () => void | Promise<void> = () => 2
In addition (this is probably the root cause): () => Promise<void> by itself is pickier than I would expect since only () => Promise<undefined> and () => Promise<any> are assignable to it. I understand that void is not an inferrable type but this case does not require void to be inferred, does it?
I suspect this problem exists because of how void interacts with parameterized types. Maybe there isn't a general solution to that, but at least I think () => Promise<2> extends () => Promise<void> would be desirable to support as a special case, given the ubiquity of the async/await pattern.
💻 Use Cases
In my case, I'm writing an implementation of forEach. It is a context that applies backpressure so I want the first-order function to be able to return a Promise, but otherwise I don't care about the output.
() => any does suffice here; () => any | Promise<any> is marginally more expressive, although from a type perspective it is exactly the same. In either case I am exposed to the pitfalls of the any type, in particular that is easy to assign such output to a value by mistake (precisely what void exists to prevent you from doing).
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
Inizia con gli esempi motivanti nell’issue e analizza come TypeScript verifica l’assegnabilità di void, Promise e delle unioni di tipi restituiti dalle funzioni. La modifica è completa quando i callback sincroni e asincroni che restituiscono valori vengono accettati nello scenario proposto di funzione di ordine superiore, senza indebolire la protezione prevista contro l’uso accidentale dei risultati scartati.
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
- Abbastanza chiara
- Idoneità per principianti
- 35/100