microsoft / microsoft/TypeScript

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

Aperta
#44,115 0 commenti 0 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

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

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 il TypeScript Playground collegato e gli esempi forniti di MatchCases e match per riprodurre il problema di inferenza. Traccia il remapping delle chiavi nei tipi mappati e l’inferenza generica; il lavoro è terminato quando l’handler Some riceve Some e la chiamata a _case accetta il tipo di variante mappato invece di K.

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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.