3
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];

I know mapped type { [P in keyof T]: T[P] } and keyof, but how do they work together and filter never?

Zen
  • 5,065
  • 8
  • 29
  • 49

1 Answers1

4

If never is present in a union, it is removed from the union. So never | "A" will just be "A".

Given this behavior, we want to construct a mapped type that has never as the type for the keys we don't want in the result, and the name of the key as the type of the key for the keys we want in the result. We do this using the conditional type T[K] extends Function ? K : never on each property. So for example:

type FunctionPropertyNamesMapped<T> = { [K in keyof T]: T[K] extends Function ? K : never }
type Test = FunctionPropertyNamesMapped<{
    a: number,
    fn: () => {}
}>
// Test is the same as
// {
//    a: never;
//    fn: "fn";
//}

Given this new mapped type, if we get a union of all key types in it (using [keyof T]) we would get the union never | "fn" which reduces to just "fn"

You can also listen to my audio explanation of something similar here

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • let `type Foo = { P1: V1, P2: V2, P3: V3 } type Bar = P1 | P2`, `Foo[Bar]` will be `V1 | V2`? I haven't seen this usage in typescript documentation. Do you have any links? – Zen Feb 25 '19 at 10:41
  • @Zen yes `Foo[keyof Foo]` will be `V1 | V2`. Looking for official docs about that... but the handbook does not say anything explicit about it as far as I can tell.. – Titian Cernicova-Dragomir Feb 25 '19 at 10:57
  • Never mind, I found it. https://stackoverflow.com/questions/49285864/is-there-a-valueof-similar-to-keyof-in-typescript – Zen Feb 25 '19 at 12:40
  • @Zen not exactly official reference, but I would take jcalz word for it. Then again you could take my word for it too :P – Titian Cernicova-Dragomir Feb 25 '19 at 12:42