microsoft / microsoft/TypeScript

Type inference not working as expected when using key remapping in mapped types

Offen
#44,115 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Needs Investigation
Vorherrschende Sprache
Go
Sterne
111k
Forks
14.3k
Ø Merge
2 T. 4 Std.
Gemergte PRs (30 T.)
132

Beschreibung

Bug Report

🔎 Search Terms

mapped types, key remapping using as, type inference

🕗 Version & Regression Information

I tested this with all versions available on typescriptlang.org/play including nightly. I was unable to test this on versions < 4.1 because key-remapping does not exist prior to version 4.1.

⏯ Playground Link

Playground link with relevant code

💻 Code
// simple Maybe implementation for testing:
type Some<T> = {__kind: "Some", boxed: T}
type None = {__kind: "None"}
type Maybe<T> = Some<T> | None
function Some<T>(value: T): Maybe<T> {return {__kind: "Some", boxed: value}}
function None(): Maybe<unknown> {return {__kind: "None"}}


type MatchCases<T extends {__kind: K}, K extends string, R> = {[V in T as V["__kind"]]: (variant: V) => R}

function match<T extends {__kind: K}, K extends string, R>(variant: T, cases: MatchCases<T, K, R>): R {
    const _case = cases[variant.__kind]
    // _case is expected to receive an argument of type K instead of V
    return _case(variant)
}

// Example 1
// Declaring types explicitly works fine, the argument type of Some handler is inferred correctly.
// I intentionally use 'string' and 'unknown' instead of "None | Some" and 'number'
// to reflect the inferred types from below:
const withExplicitTypes = match<Maybe<number>, string, unknown>(Some(5), {
    None: () => 1,
    Some: ({boxed}) => boxed * 2
})

// Example 2
// The compiler infers the exact same types as above:
const withTypeInference = match(Some(5), {
    None: () => 1,
    // But the argument to Some-handler cannot be inferred correctly:
    Some: some => some.boxed 
})
🙁 Actual behavior

I might be misusing the key remapping feature, but while implementing the above mentioned match function, I ran into following behavior:

  • calling match above with explicit generic types works as expected (Example 1)
  • the compiler infers the exact same types when not provided explicitly, but the arguments to the case handlers are inferred as any (Example 2), even that intellisense initially proposes the correct type while typing (see [1])
  • when trying to call a case function in the body of match, the expected type of the argument is K instead of T.
🙂 Expected behavior
  • I'm expecting _case to require an argument of type V as declared in the mapped type ((variant: V) => R).
  • Based on the inferred types of MatchCases, the argument of the Some-handler should be inferred to type Some<number>.

[1]:

image

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne mit dem verlinkten TypeScript Playground sowie den bereitgestellten MatchCases- und match-Beispielen, um den Inferenzfehler zu reproduzieren. Verfolge das Remapping von Schlüsseln in gemappten Typen und die generische Inferenz; fertig ist die Untersuchung, wenn der Some-Handler Some erhält und der Aufruf von _case den gemappten Variantentyp statt K akzeptiert.

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
45/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.