Tips for obtaining a subset of `keyof T` where the value, T[K], refers to callable functions in Typescript

Is there a way to filter keyof T based on the type of T[keyof T]?

This is how it should function:

type KeyOfType<T, U> = ...

KeyOfType<{a: 1, b: '', c: 0, d: () => 1}, number> === 'a' | 'c'

KeyOfType<{a: 1, b: '', c: 0: d: () => 1}, string> === 'b'

KeyOfType<{a: 1, b: '', c: 0: d: () => 1}, Function> === 'd'

Can this be achieved?

Answer ā„–1

Utilize the as clause within a mapped type to selectively filter out keys of a type:

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

The as clause in a mapped type enables us to modify the key's name. If a key is mapped to never, it will be excluded from the resulting type. Further information can be found in this PR

Before TS 4.1

This version remains functional post 4.1, although the alternative approach is easier to comprehend.

You can achieve this by utilizing conditional and mapped types

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

Let's delve deeper into the concept.

We start with a mapped type that mirrors the properties of the original type T, essentially a basic standard mapped type:

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

T[P] refers to a type query denoting the type of the key P in type T. We can simplify this to just P, indicating that the type of the new property matches its name:

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

We introduce a type query to retrieve all the keys of the type again. Typically, T[keyof T] retrieves all property types of a type. Applying this to our mapped type (where property types match key names), we essentially revert back to keyof T:

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

Now we incorporate a conditional type to selectively choose not always select P. By setting the type of the property in the mapped type to never if it does not meet a specified constraint (T[P]), we effectively employ the rule A | never == A.

To specify the constraint, we add an additional generic parameter U and apply a conditional type defined as

T extends U ? TypeIfTrue: TYpeIfFalse
. Combining these elements results in:

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

Answer ā„–2

This explanation is fantastic!

In addition, I want to mention that you have the flexibility to designate a default type U that can be any key of T, as shown below:

type Keys<T> = {[P in keyof T]: T[P]}[typeof P]

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

I introduced U = Keys<T> into the generic definition of KeyOfType

Your insights are greatly appreciated!

Answer ā„–3

If you're looking for a way to filter out values based on their type, consider using the PickByValue method found in the utility-types library.

Check out PickByValue here!

import { PickByValue } from 'utility-types';

class ExampleClass {
  propertyOne = 'one';
  propertyTwo = 'two';
  methodOne() {
    console.log('I am a method');
  }
  methodTwo() {
    console.log('I am another method');
  }
}

type filteredMethods = keyof PickByValue<ExampleClass, Function> // 'methodOne' | 'methodTwo'

In this scenario, using

keyof PickByValue<ExampleClass, () => void>
is an alternative option if you prefer specifying a specific function type.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Identified the category

How can I retrieve the default option from a list of options? type export type Unpacked<T> = T extends Array<infer U> ? U : T; interface getDefaultValue?: <T extends Option[]>(options: T) => Unpacked<T>; Example const options = ...

"The ion-label in Ionic2 is cutting off some of the text and not displaying it

I'm currently facing an issue with ion-label inside ion-item. The description is not properly displaying and instead, it shows dot-dot.. ..I need the entire text to be visible. Is there any solution available? <ion-card *ngFor="let product of prod ...

What methods are available for altering state in Server Actions in NextJS 13?

Struggling to Implement State Change in NextJS 13 Server Actions I have encountered a challenge with altering state within the execution of server actions in NextJS 13. The scenario involves an actions.ts file located at the root of the app directory. Cur ...

What is the reason behind the ability to assign any single parameter function to the type `(val: never) => void` in TypeScript?

Take a look at the code snippet below interface Fn { (val: never): void } const fn1: Fn = () => {} const fn2: Fn = (val: number) => {} const fn3: Fn = (val: { canBeAnyThing: string }) => {} Despite the lack of errors, I find it puzzling. For ...

Having trouble with the Angular Language Service extension in VS Code for Angular-16?

Upon transitioning to Angular 16, I encountered errors while attempting to edit the components HTML due to the malfunctioning of the Angular Language Service extension. [Info - 09:41:11] Angular language server process ID: 18032 [Info - 09:41:11] Using t ...

Updating row values in an Angular table

I have a reusable table with the [cellData]="row" attribute to populate each cell on the table (see sample table in the screenshot). My question is, how can we replace the null values on the template with "---" so that instead of displ ...

Difficulty Converting Array of Objects to Proper Type with Q.Promise and KO.mapping

I have encountered an issue while trying to filter an observable array. It seems that the ko.utils.arrayFilter method is converting all my model's field names to lowercase, causing unexpected behavior. I should mention that this project involves Types ...

Customizing Material UI CSS in Angular: A Guide

Recently, while working with the Mat-grid-tile tag, I noticed that it utilizes a css class called .mat-grid-tile-content, which you can see below. The issue I am encountering is that the content within the mat-grid-tile tag appears centered, rather than f ...

Replicating an array of typed objects in Angular2

I have a collection of specific object types and I'm looking to duplicate it so that I can work on a separate version. In order for the configuratorProduct to function correctly, I need to provide it with a copy of the listProducts values: listPro ...

Testing vue-router's useRoute() function in Jest tests on Vue 3

Struggling with creating unit tests using Jest for Vue 3 components that utilize useRoute()? Take a look at the code snippet below: <template> <div :class="{ 'grey-background': !isHomeView }" /> </template> &l ...

What is the best way to encode a type that necessitates a specific superclass and interface implementation?

In order to achieve my goal, I need to extend a library class that is part of the React components I am creating. Here's an example of the original library class I'm working with: class ReactComponent<T, U> { render() { return "some ht ...

Is there a way to turn off the warning overlay in a React application?

Iā€™m currently using react-app-rewired and I am trying to figure out how to turn off the overlay that displays Typescript warnings whenever I compile. It seems like some warnings that are not caught by the VSCode Typescript checker pop up on this overlay ...

Tips for troubleshooting the error message: "The relative import path "$fresh/dev.ts" is not prefaced with / or ./ or ../"

My editor is showing a TypeScript error for a Deno module I am working on. The import path "$fresh/dev.ts" should be prefixed with / or ./ or ../ I have an import_map.json file set up with the following content. { "imports": { "$fre ...

Dealing with Dependency Injection Problem in Angular 6

I am grappling with a challenge in my cross-platform Angular application. The crux of the issue is determining the platform on which the app is running and accordingly injecting services. Below is how I've structured it: @NgModule({ providers: [ ...

Explain the TypeScript type where the keys of an object not found in an array should correspond to the type of another object

Currently, I am developing a utility function called copyKnownProperties that is responsible for copying properties from one class or object to another only if the key exists on both objects. In the example provided, you can observe an attempt to copy prop ...

Extracting information from an Observable in Angular: A comprehensive guide

I am currently working on an Angular application that interacts with a server through RESTful requests, and receives a JSON stream response containing objects of a specific type. The interface for these objects is as follows: export interface Personal { ...

The selected check icon does not appear in the console log

Objective: Display preselected data from selected checkbox in console.log Issue: The preselected data is not appearing in the console log. When manually checked, the data shows up. What am I missing? About Me: I am a beginner in Angular. Thank ...

Trigger a method within a component when there is a change in the Vuex state

I need to trigger a method inside a component whenever the vuex state changes in TypeScript and Vue.js. While I can access the vuex state value using getters in the template, I am unsure how to access the data within the component class. The vuex state is ...

How can I confirm if a class is an instance of a function-defined class?

I have been attempting to export a class that is defined within a function. In my attempts, I decided to declare the class export in the following way: export declare class GameCameraComponent extends GameObject { isMainCamera: boolean; } export abstra ...

Asynchronous task within an if statement

After pressing a button, it triggers the check function, which then executes the isReady() function to perform operations and determine its truth value. During the evaluation process, the isReady() method may actually return false, yet display "Success" i ...