microsoft / microsoft/TypeScript

Handle union type of void and Promise<void> in return types

Aperta
#43,921 6 commenti 9 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

In Discussion Suggestion
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

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. 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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.