microsoft / microsoft/TypeScript
Type inference not working as expected when using key remapping in mapped types
オープン
まだ誰も着手していません。
Needs Investigation
- 主要言語
- Go
- スター
- 111k
- フォーク
- 14.3k
- 平均マージ
- 1日 19時間
- マージ済み PR(30日)
- 117
説明
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
matchabove 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 isKinstead ofT.
🙂 Expected behavior
- I'm expecting
_caseto require an argument of typeVas 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 typeSome<number>.
[1]:

コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
リンクされている TypeScript Playground と、提供されている MatchCases および match の例から始めて、推論の失敗を再現します。mapped type におけるキーのリマッピングとジェネリック推論を追跡します。Some ハンドラーが Some を受け取り、_case の呼び出しが K ではなくマップされたバリアント型を受け入れれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- typescript
- 領域
- compilers
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 45/100