microsoft / microsoft/TypeScript

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

Abierto
#35,162 8 comentarios 10 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Needs Investigation
Lenguaje dominante
Go
Estrellas
111k
Forks
14.3k
Merge medio
1 d 19 h
PR fusionados (30 d)
117

Descripción

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.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza abriendo la reproducción enlazada de TypeScript Playground y compara las llamadas de Case 3 con los casos que funcionan y con el comportamiento en TypeScript 3.3.3. Investiga la inferencia genérica para el objeto en línea utilC cuando callable recibe computeCallableB(100). Se considera terminado cuando la llamada en línea no produce errores e infiere t como number, sin requerir una variable intermedia.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
typescript
Área
compilers
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
42/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.