How to get a subset of `keyof T` whose value, T[K] are callable functions in Typescript

You can use the as clause of a mapped type to filter the keys of a type:

type KeyOfType<T, V> = keyof {
    [P in keyof T as T[P] extends V? P: never]: any
}

The as clause of a mapped type allows us to manipulate the name of the key. If the key is mapped to never the key gets removed from the resulting type. You can find more info in this PR

Pre TS 4.1

This version still works in post 4.1, just the other way is easier to read.

You can do this using conditional and mapped types

type KeyOfType<T, U> = {[P in keyof T]: T[P] extends U ? P: never}[keyof T]

Let’s break things down a little bit.

We can start with a mapped type, that has the same properties of the same type as the original T, this a simple standard mapped type:

type KeyOfType<T> = { [P in keyof T]: T[P] } // New Type same as the original

T[P] is a type query and means the type of the key P in type T. We can change this to just P, meaning that the type of the new property is the same as it’s name:

type KeyOfType<T> = { [P in keyof T]: P }
// So
KeyOfType<{ a: number, b: string }> == { a: 'a', b: 'b' }

We can add a type query to this type to again get all the keys of the type. Generally a construct T[keyof T] gets all the property types of a type. Applying this to our mapped type which has the property types the same as the key names we basically get back to keyof T:

type KeyOfType<T> = { [P in keyof T]: P }[keyof T]
// So
KeyOfType<{ a: number, b: string }> ==  'a'|'b' 

Now we can add a conditional type to o not always select P. Since A | never == A we can set the type of the property in the mapped type to never if the type of the original property (T[P]) does not meet a certain constraint.

To express the constraint we add an extra generic parameter U and we use a conditional type which has the form T extends U ? TypeIfTrue: TYpeIfFalse. Putting it together we get:

type KeyOfType<T, U> = {[P in keyof T]: T[P] extends U ? P: never}[keyof T]

Leave a Comment