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

Leveraging Services in Classes Outside of Angular's Scope

I have a scenario where I am working with Angular v7.3.5 and I need to utilize a Service in a non-Angular class. Below is an example of what I'm trying to achieve: foo.model.ts import { Foo2Service } from './foo2.service'; // Definition fo ...

When importing a module, the function in the ts file may not be recognized or located

While attempting to create a VSTS (Azure Devops) Extension, I encountered a perplexing issue. Within my HTML page, I have a button element with an onclick listener: <!DOCTYPE html> <head> <script type="text/javascript"> VS ...

Securing Angular 2 routes with Firebase authentication using AuthGuard

Attempting to create an AuthGuard for Angular 2 routes with Firebase Auth integration. This is the implementation of the AuthGuard Service: import { Injectable } from '@angular/core'; import { CanActivate, Router, Activated ...

"Slow loading times experienced with Nextjs Image component when integrated with a map

Why do the images load slowly on localhost when using map, but quickly when not using it? I've tried various props with the Image component, but none seem to solve this issue. However, if I refresh the page after all images have rendered once, they ...

The data type 'string' cannot be assigned to the data type 'undefined'

These are the different types available: export type ButtonProperties = { style?: 'normal' | 'flat' | 'primary'; negative?: boolean; size?: 'small' | 'big'; spinner?: boolean; } export type ...

Angular module cannot be located

While working on implementing a chat feature for my project using sockJS and STOMP, I encountered several challenges with installing these two libraries. Despite attempting various methods such as installation from index.html, npm install, and manual downl ...

Design buttons that are generated dynamically to match the style

I have a challenge in styling dynamically generated buttons. I've developed a component responsible for generating these dynamic buttons. const TIMER_PRESETS: Record<string, number> = { FIFTHTEENSEC: 15, THIRTYSEC: 30, FORTYFIVESEC: 45, ...

Testing React Components - The `useClient` function should only be used within the `WagmiConfig` component

In my Next.js app with TypeScript, Jest, and React testing library, I encountered an error while trying to test a component. The error message states that `useClient` must be used within `WagmiConfig`. This issue arises because the `useAccount` hook relies ...

Converting JSON to string in Typescript is causing an error where type string cannot be assigned to type '{ .. }'

Here's the code snippet I'm working with: interface ISource extends IdModel { source_type_id: number; network_id: number; company_connection_id: number; feed_id: number; connection_id: number; feed_ids: number[]; name: string; tag ...

Is it possible to utilize a variable for binding, incorporate it in a condition, and then return the variable, all while

There are times when I bind a variable, use it to check a condition, and then return it based on the result. const val = getAttribute(svgEl, "fill"); if (val) { return convertColorToTgml(val); } const ancestorVal = svgAncestorValue(svgEl, "fill"); if (a ...

Error Type: nextjs 13 - children function TypeError

Welcome to the Home page: export default async function Home() { # console.log(data) it is populated const { data } = getAllArts(); return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> < ...

A Fresh Approach for Generating Unique UUIDs without Bitwise Operators

To generate UUIDs in TypeScript, I have implemented a method inspired by the solution provided on Stack Overflow. The code effectively converts JavaScript to TypeScript. generateUUID(): string { let date = new Date().getTime(); if (window.performa ...

Managing form input in Ionic2 components from external sources in Angular2

Hello there, I am currently facing an issue with managing form validation along with proper styling for nested forms. Here's what I'm aiming to achieve: I have a Page that displays Tabs components with four tabs. Each tab represents a separate @ ...

Utilize key-value pairs to reference variables when importing as a namespace

Is it feasible to utilize a string for performing a lookup on an imported namespace, or am I approaching this the wrong way? Consider a file named my_file.ts with contents similar to: export const MyThing: CustomType = { propertyOne: "name", ...

How can a parent component update a child component's prop value in VUE?

I'm facing an issue with managing dynamic props in Vue with TypeScript. Below is my parent component: <script setup lang="ts"> const friends = [ { id: "emanuel", name: "Emanuella e", phone: "08788 ...

What about a toggle for read-only TypeScript everywhere? (parameters in functions)

Is there a method, whether through a macro library, an eslint rule, a tsconfig setting, a special global.d.ts file, or some other means, to automatically set function arguments as readonly by default? // I wish for the compiler to transform this: functio ...

Is there a way for me to view the output of my TypeScript code in an HTML document?

This is my HTML *all the code has been modified <div class="testCenter"> <h1>{{changed()}}</h1> </div> This is my .ts code I am unsure about the functionality of the changed() function import { Component, OnInit } f ...

Reactive form allows you to easily format dates

Currently, the date displayed is 1/4/2022. We need it to display in the format 01/04/2022. Can we achieve this formatting using reactive forms with the sample Model form provided below? Thank you. How can we format it when starting from transactionStartD ...

Effortlessly collapsing cards using Angular 2 and Bootstrap

Recently delving into Angular 2 and Bootstrap 4, I set up an about page using the card class from Bootstrap. Clicking on a card causes it to expand, and clicking again collapses it. Now, I want to enhance this by ensuring that only one card is open at a ti ...

What steps do I need to take for the function to accurately determine the return type?

class Foo { name: string; constructor({name}: {name: string}) { this.name = name; } } class Bar<T extends Foo> { foo: T; constructor({foo}: {foo: T}) { this.foo = foo; } } class CustomFoo extends Foo { xxx: string; constr ...