I came across this TypeScript snippet yesterday:
type KeysOfUnion<T> = T extends infer P ? keyof P : never
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
It makes me confused: What is being inferred here? I've created a mental model that using infer
keyword is similar to destructuring in JS – of course this parallel is far from perfect, but it did helped me a lot.
But I can't see any context here: T extends infer P
? What am I infering? What is the relation between P
and T
?
After testing the snippet in TS playground, I would guess that it have something to do with separating the types that have a constructor from those that don't have one.
type test1 = KeysOfUnion<{ name: "Bill"}> // name
type test2 = KeysOfUnion<"Orange"> // number | typeof Symbol.iterator | "toString" | "charAt" etc.
type test3 = KeysOfUnion<1> // "toString" | "valueOf" | "toFixed" etc.
type test4 = KeysOfUnion<true> // "valueOf"
type test5 = KeysOfUnion<Object> // keyof Object
type test6 = KeysOfUnion<object> // never
type test7 = KeysOfUnion<{}> // never
The object
and {}
behavior is a little confusing, but I guess it is explained here: Difference between 'object' ,{} and Object in TypeScript
type test8 = KeysOfUnion<undefined> // never
type test9 = KeysOfUnion<null> // never
type test10 = KeysOfUnion<never> // never
type test11 = KeysOfUnion<void> // never
But the test results are probably rather related to keyof P
– still have no clue why T extends infer P
is used here.
Can anyone explain?