Extract keys from a list of interface keys to create a sub-list based on the type of value

Issue

Can the keys that map to a specified value type be extracted from a TypeScript interface treated as a map?

For example, consider the WindowEventMap in lib.dom.d.ts...

interface WindowEventMap 
    extends GlobalEventHandlersEventMap, WindowEventHandlersEventMap {
    "abort": UIEvent;
    "afterprint": Event;
    "beforeprint": Event;
    "beforeunload": BeforeUnloadEvent;
    "blur": FocusEvent;
    // ...
}

The keyof operator can retrieve all keys:

type FullKeyList = keyof WindowEventMap;
// yields: FullKeyList = "'abort' | 'afterprint' | 'beforeprint' | ..and 93 more"

Is there an expression to filter only the keys mapping to the value type KeyboardEvent?:

// Objective:
type SubKeyList = SomeExpression<WindowEventMap, KeyboardEvent>;
// would result in: SubKeyList = "'keyup' | 'keypress' | 'keydown'"

Attempted Solution

A Conditional Type expression exists in TypeScript:

T extends U ? X : Y

A possible solution could be:

type Filter<K extends keyof M, V, M> = M[K] extends V ? K : never;

It filters individual types:

// Passes this test:
type FilteredExample1 = Filter<'keyup', KeyboardEvent, WindowEventMap>;
// outputs FilteredExample1 = "'keyup'"

// Passes this test:
type FilteredExample2 = Filter<'blur', KeyboardEvent, WindowEventMap>;
// outputs FilteredExample2 = "never"

But can it filter a union of types and return a new union? It seems promising at first:

// Passes this test:
type FilteredUnionExample1 = Filter<'keyup' | 'keydown', KeyboardEvent , WindowEventMap>;
// outputs FilteredUnionExample1 = "'keyup' | 'keydown'"

However, the union fails if one or more members fail:

// Fails this test:
type FilteredUnionExample2 = Filter<'keyup' | 'keydown' | 'blur', KeyboardEvent, WindowEventMap>;
// outputs FilteredUnionExample2 = "never" (not sub-union "'keyup' | 'keydown'")

// So, it also fails the desired usage:
type AllKeysUnion = keyof WindowEventMap;
type FilteredUnionExample3 = Filter<AllKeysUnion, KeyboardEvent , WindowEventMap>;
// outputs FilteredUnionExample3 = "never" (not sub-union "'keyup' | 'keypress' | 'keydown'")

Is there a solution to this challenge?

Answer №1

A commonly used term for this concept is KeysMatching:

type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];

type SubKeyList = KeysMatching<WindowEventMap, KeyboardEvent>
// type SubkeyList = "keydown" | "keypress" | "keyup"

Link to code on TypeScript Playground

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

Error: Unable to execute function abc, it is not defined as a function in this context

Having trouble with a fronted or JavaScript issue where it can't find the defined function in the file. I'm working on integrating Apple Pay and need to call the back-end API based on a specific event. Here is my code. ACC.payDirect = { _autoload ...

Unraveling the mysteries of an undefined entity

When the variable response is undefined, attempting to retrieve its property status will result in an error: Error: Unable to access property 'status' of undefined const { response, response: { status }, request, config, } = error as A ...

At what point is it appropriate for a class to incorporate an interface?

Currently working on a project and I've noticed developers using TypeScript in the following way: export class Ledger implements ILedger { LedgerID: number; CashAmmount: number; Units: number; ...

"An issue has been identified where TSLint and VSCode fail to display red underlines in

I am currently working on a single .ts file where I am experimenting with configuring tslint and tsconfig. To test the configuration, I intentionally added extra spaces and removed semicolons. Despite running the command tslint filename.ts and detecting e ...

Managing conflicting versions of React in a component library created with Webpack and Storybook

My goal is to create a React component library on top of MUI using Storybook and TypeScript. Since Storybook is based on Webpack (which includes SASS files), I'm utilizing Webpack to build the bundle because TSC can't compile those files. Subsequ ...

Executing functions in Vue TypeScript during initialization, creation, or mounting stages

Just a few hours ago, I kicked off my Vue TypeScript project. I've successfully configured eslint and tslint rules to format the code as desired, which has left me quite pleased. Now, I'm curious about how to utilize the created/mounted lifecycl ...

After extraction from local storage, the type assertion is failing to work properly

I have a unique situation in my Angular project where I have stored an array of data points in the local storage. To handle this data, I have created a custom class as follows: export class Datapoint { id: number; name: string; // ... additional pr ...

The parameter 'To' cannot be assigned the argument of type '{pathname: string; shipment: TShipment;}'

Is there anyone who can assist me with a Typescript issue I'm currently facing? Upon running tsc in my project, I encountered the following error: I am getting the error saying that 'Argument of type '{ pathname: string; item: Item; }' ...

What is the method for extracting children from a singular object in json-server rather than an array?

I am currently utilizing json-server as a mock-backend to fetch child data from a single object. The main table is called sentinel and the secondary table is named sensor https://i.sstatic.net/1BrRq.png https://i.sstatic.net/3lOVD.png It can be observ ...

Interacting between components using Angular 2 services

I am attempting to implement bidirectional service communication using Angular. I have followed the instructions provided in the documentation here: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service interactio ...

Is it possible to use Date as a key in a Typescript Map?

Within my application, I have a requirement for mapping objects according to specific dates. Given that typescript provides both the Map and Date objects, I initially assumed this task would be straightforward. let map: Map<Date, MyObject> = new M ...

What is the best way to implement a switch case with multiple payload types as parameters?

I am faced with the following scenario: public async handle( handler: WorkflowHandlerOption, payload: <how_to_type_it?>, ): Promise<StepResponseInterface> { switch (handler) { case WorkflowHandlerOption.JOB_APPLICATION_ACT ...

Sharing information between different pages in NEXT.js version 14

After performing a fetch and receiving a successful response containing data as an object, I use router.push to redirect the page to another one where I want to display the fetched data. const handleSubmit = async (event: any) => { event.preventDefa ...

How come I am unable to define global electron variables in my HTML when using Typescript?

I am currently working on a personal project using Electron and Typescript. Both my Main.js and Renderer.js files are in Typescript and compiled. My issue is with the "remote" variable in my template (main.html). While it works within the template, I can&a ...

When 'Interval.after' is invoked within the library, Luxon throws an error message stating "Invalid Interval."

Encountering a strange issue with Luxon when the Interval.after method is invoked within the library. const interval = Interval.after(dateTime, duration); The following log pertains to the application DateTime__Duration, with the second line representing ...

Simulating chained responses in Express using JEST

I am relatively new to using jest and typescript, currently working on creating a unit test for a controller function in jest import { Request, Response } from 'express'; const healthCheck = (_req: Request, _res: Response) => { const value ...

TypeScript's version of Java's enum (or C#'s structure)

I'm facing the challenge of creating an enum in Typescript that mimics the functionality of Java enums. In TypeScript, only integer-based enums like C# are supported, unlike in Java where we can have custom objects with non-integer related properties ...

Enhance the variety of types for an external module in TypeScript

I am in the process of migrating an existing codebase from a JavaScript/React/JSX setup to TypeScript. My plan is to tackle this task file by file, but I have a question regarding the best approach to make the TypeScript compiler work seamlessly with the e ...

Why isn't Gzip compression working in webpack? What am I missing?

After comparing the compression results of manual webpack configuration and create-react-app for the same application, it became clear that create-react-app utilizes gzip compression, resulting in a significantly smaller final bundle size compared to manua ...

Receiving contextual information from Microsoft Teams within an Angular application integrated as a tab

I am currently integrating an Angular website into a Microsoft Teams tab. In order to perform certain computations, I need to retrieve the Team ID. To achieve this, I have recently added npm install --save @microsoft/teams-js. Below is the code snippet th ...