microsoft / microsoft/TypeScript

Assigning a callable return type of a generic function directly to some generic parameter results in error

Aperta
#35,162 8 commenti 10 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Needs Investigation
Lingua principale
Go
Stelle
111k
Fork
14.3k
Merge medio
2g 4h
PR unite (30g)
132

Descrizione

TypeScript Version: above 3.3.3

Code

To see the actual behavior depending on a version of TS it is better to open the code below in the playground

type Callable<T> = { (t: T): any; };


declare function utilA<T>(
  callable: Callable<T>,
  fn: (t: T) => any
): any

declare function utilB<T>(
  callable: Callable<T>,
  params: {
    fn: (t: T) => any
  }
): any

declare function utilC<T>(
  params: {
    callable: Callable<T>,
    fn: (t: T) => any
  }
): any

/**
 * Case 1: directly assigning a callable
 */

declare const callable: Callable<number>

utilA(
  callable,
  t => t.toExponential() // t is a number, OK
)

utilB(
  callable,
  {
    fn: t => t.toExponential() // t is a number, OK
  }
)

utilC({
  callable,
  fn: t => t.toExponential() // t is a number, OK
})

/**
 * Case 2: directly assigning the result of a function returning a callable
 */

declare function computeCallableA(): Callable<number>

utilA(
  computeCallableA(),
  t => t.toExponential() // t is a number, OK
)

utilB(
  computeCallableA(),
  {
    fn: t => t.toExponential() // t is a number, OK
  }
)

utilC({
  callable: computeCallableA(),
  fn: t => t.toExponential() // t is a number, OK
})

/**
 * Case 3: directly assigning the result of a generic function returning a callable
 */

declare function computeCallableB<T>(t: T): Callable<T>

utilA(
  computeCallableB(100),
  t => t.toExponential() // t is a number, OK
)

utilB(
  computeCallableB(100),
  {
    fn: t => t.toExponential() // t is a number, OK
  }
)

utilC({
  callable: computeCallableB(100), // Error
  fn: t => t.toExponential() // t is an unknown
})

/**
 * The last case is strange IMO. The result type of `computeCallableB` is immediately known.
 * 
 * Hovering the last `utilC` shows the following:
 * 
 * function utilC<number>(params: {
 *   callable: Callable<number>;
 *   fn: (t: number) => any;
 * }): any
 * 
 * Despite the fact that TS infers generic `T` of `utilC`, assignment to `callable` param
 * gives: "Type 'Callable<number>' is not assignable to type 'Callable<unknown>'.".
 * 
 * It is hard to understand whats going on. More than that, everything works in TS v3.3.3.
 * In newer versions the behavior is broken.
 * 
 * This has an impact on the library I help to maintain. Such use cases are not rare and
 * people are forced to assign the result of `computeCallableB(100)` to a variable first,
 * and then to a `callable` parameter, which affects DX:
 */

const computeCallableBResult = computeCallableB(100)

utilC({
  callable: computeCallableBResult, // This works!
  fn: t => t.toExponential() // t is a number, OK
})

Expected behavior:

The invocation of

utilC({
  callable: computeCallableB(100), // Error
  fn: t => t.toExponential() // t is an unknown
})

gives no errors.

Actual behavior:

Strange behavior in versions above 3.3.3.

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 aprendo la riproduzione collegata di TypeScript Playground e confronta le chiamate di Case 3 con i casi funzionanti e con il comportamento in TypeScript 3.3.3. Analizza l’inferenza generica per l’oggetto inline utilC quando callable riceve computeCallableB(100). Il lavoro è completato quando la chiamata inline non produce errori e inferisce t come number, senza richiedere una variabile intermedia.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
typescript
Ambito
compilers
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
42/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.