microsoft / microsoft/TypeScript
Assigning a callable return type of a generic function directly to some generic parameter results in error
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.3k
- Ø Merge
- 1 T. 19 Std.
- Gemergte PRs (30 T.)
- 117
Beschreibung
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.
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Öffne zunächst den verlinkten TypeScript Playground-Repro und vergleiche die Aufrufe in Case 3 mit den funktionierenden Fällen und dem Verhalten in TypeScript 3.3.3. Untersuche die generische Inferenz für das Inline-Objekt utilC, wenn callable computeCallableB(100) erhält. Als erledigt gilt die Aufgabe, wenn der Inline-Aufruf keine Fehler erzeugt und t als number inferiert wird, ohne eine Zwischenvariable zu benötigen.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- typescript
- Bereich
- compilers
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 42/100