Open
Description
Bug Report
🔎 Search Terms
- indexing related keys of mapped types
- supertype mapped type
- excluding keys mapped type
🕗 Version & Regression Information
- This is the behavior in every version I tried (4.4, 4.5, 4.6-dev), and I reviewed the FAQ for entries about mapped types
⏯ Playground Link
Playground link with relevant code
💻 Code
// SuperType<T, C> constructs a super type of T by filtering out properties of T that do not extend C
// Option 1 (the most correct, removes keys)
type SuperType<T, C> = { [K in keyof T as (T[K] extends C ? K : never)]: T[K] }
// Option 2 (not correct, resets fields to never)
// type SuperType<T, C> = { [K in keyof T]: T[K] extends C ? T[K] : never }
// Option 3 (Option 1 and 2 combinded)
// type SuperType<T, C> = { [K in keyof T as (T[K] extends C ? K : never)]: T[K] extends C ? T[K] : never }
// Option 4 (I saw this used in the wild)
// type SuperType<T, C> = Pick<T, { [K in keyof T]: T[K] extends C ? K : never }[keyof T]>
type Nums<T> = SuperType<T, number>
// examples
type A = { a: 10, b: true }
type CA = Nums<A> // Option 1, 3, 4: { a: 10 }
// Option 2: { a: 10, b: never }
type CAK = keyof CA // Option 1, 3, 4: 'a'
// Option 2: 'a' | 'b'
function f<T, U extends keyof Nums<T>>(t: T, m: U) {
// This should be reduced to T, because T extends SuperType<T, *>
type ShouldBeT = T & Nums<T>
// This should be reduced to U, since it is keysof T extends U
type ShouldBeU = U & keyof T
// n1 expected to be bounded to number, because m is keyof Nums<T>
const n1 = t[m] // Option 1, 2: T[U]
// Option 3: any, error Type 'U' cannot be used to index type 'T'.(ts2536)
n1.toFixed() // Option 1, 2, 4: bounded to unknown
// Option 3: any type
// same, n1 expected to be bounded to number, but with a hint
const n2 = t[m] as SuperType<T, number>[U] // Option 1, 2: ok
// Option 3: Type 'U' cannot be used to index type 'T'.(2536)
n2.toFixed() // Option 1, 4: bounded to unknown
// Option 2: bounded to number (success!)
// Option 3: bounded to number (because of type assertion)
}
🙁 Actual behavior
- TS cannot relate a key that is a key of a supertype (with same or lesser keys) constructed by mapped type removing some keys
- TS cannot index a generic type with a key of its generic supertype
🙂 Expected behavior
- TS can relate a key that is a key of a supertype (with same or lesser keys) constructed by mapped type removing some keys
- TS can index a generic type with a key of its generic supertype
Reasoning
- any field with key
U
ofSuperType<T, C>
is also a key ofT
and its type extendsC
- therefore
T[U]
should be allowed becauseU
iskeyof T
- therefore
T[U]
should beC
becauseT[K]
extendsC
for any key inU
That is a very common task that you need to filter out some properties from a type. I saw people call it a subtype when in fact it is a supertype. But weirdly enough indexing be keys of the mapped type is not allowed. I saw it as a regression in a big codebase when upgraded to 4.5, but turned out the values were considered any
. So now they're bounded to unknown
which is better but still cannot do much further.