What is the process for sorting record keys depending on their values?

Within my dataset, I possess this particular record:

interface TheRecord extends TheRecordType {
  a: { typeA: 'string' },
  b: { typeB: 123 },
  c: { typeA: 'string' },
}

type TheRecordType = Record<string, TypeA | TypeB>

type TypeA = { typeA: string }
type TypeB = { typeB: number }

My intention is for my function to exclusively accept keys whose values match TypeA

doStuff('b'); //this should fail

function doStuff(arg: keyof FilteredForTypeA): void {
  ...
}

Here's the strategy I utilize to filter them appropriately

type FilteredForTypeA = { [k in keyof TheRecord]: TheRecord[k] extends TypeA ? TheRecord[k] : never }

Answer №1

It seems there are a few complexities at play here, prompting me to provide an answer as the existing questions do not directly address the situation.

When dealing with a type that includes an index signature, extracting only the "known" literal keys of the object proves challenging if those keys are subtypes of the index signature. For example,

keyof {[k: string]: any, foo: any}
results in just string, where "foo" is fully encompassed within that. To selectively extract only the known literal keys, a conditional type trick can be employed, as demonstrated in this related question:

type RemoveIndex<T> = {
    [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]
};

type KnownKeys<T> = keyof RemoveIndex<T>;

On the other hand, isolating only the keys whose associated values meet specific criteria can be achieved through mapped-conditional-lookup, outlined in this related question:

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

Combining these approaches yields:

type KnownKeysMatching<T, V> = KeysMatching<Pick<T, KnownKeys<T>>, V>

Verification of this implementation demonstrates its intended functionality:

function doStuff(arg: KnownKeysMatching<TheRecord, TypeA>): void {
}

doStuff('a'); // okay
doStuff('b'); // error!
doStuff('c'); // okay
doStuff('d'); // error! 

Observe how the variable arg cannot be assigned 'b' as per specifications, and similarly cannot be 'd' or any other "unknown" string, despite TheRecord having a string index signature. If adjustments are needed for handling 'd' differently, it might require additional modifications beyond the initial scope of the inquiry.

Playground link to code

Answer №2

By utilizing a slightly altered version of KnownKeys to remove keys whose values also never expand, the final result appears as follows:

interface UpdatedRecord extends RecordType {
  a: { typeA: 'string' },
  b: { typeB: 123 },
  c: { typeA: 'string' },
}

type RecordType = Record<string, TypeA | TypeB>
type KnownKeys<T> = {
  [K in keyof T]: string extends K ? never : number extends K ? never : T[K] extends never ? never : K
} extends { [_ in keyof T]: infer U } ? U : never;

type TypeA = { typeA: string }
type TypeB = { typeB: number }

function performAction(arg: KnownKeys<FilteredForTypeA>): void {

}

type FilteredForTypeA = { [k in keyof UpdatedRecord]: UpdatedRecord[k] extends TypeA ? UpdatedRecord[k] : never }

performAction('b'); // error!

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

Tips for Specifying that Type T Needs to Contain the Field "ID"

This code snippet demonstrates how to create a dictionary using a generic method in C#: public static void createDictionary<T>(IEnumerable<T> myRecords) where T: T.ID // Wont Compile { IDictionary<int, T> ...

The term 'XInterface' is not recognized in Typescript

Having some issues with my code here. I've defined a class and an interface, but Visual Studio is giving me an error saying it can't find the name 'RouteInterface'. I'm stumped as to why. import {Student} from './student&apos ...

Troubleshooting ion-radio loop error in Ionic 2

I am encountering an issue with the ion-radio component in Ionic 2. The problem is that when the component retrieves data from a service using HTTP and assigns it to the data property within the ngOnInit lifecycle hook, the radio buttons are not able to b ...

Choose the type of function declaration over a function expression

I am interested in specifying the type for a function declaration, as shown below: function foo(){ } However, I have been struggling with how to achieve this. Instead, I have resorted to converting the function declaration into an expression like this: ...

Testing the selection of dropdown values in Angular2 using unit tests

I have a dropdown menu for selecting countries. It defaults to a pre-selected value, and when the user changes it, they are redirected to the corresponding country page. However, I am facing an issue while trying to unit test the default value in the sele ...

TypeScript Firestore Reducer: A more efficient way to

I've been working on my reducers and have come across this piece of code: import { Reducer, combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux'; import { firebaseReducer } from 'react-redux-fire ...

Guide on merging non-modular JavaScript files into a single file with webpack

I am trying to bundle a non-modular JS file that uses jQuery and registers a method on $.fn. This JS must be placed behind jQuery after bundling. Here is an example of the structure of this JS file: (function($){ $.fn.splitPane = ... }(JQuery) If y ...

What are the necessary peer dependencies for Angular-compatible projects?

In the process of integrating RxJS as a peer dependency for my Angular 6 project, I encountered an interesting declaration method. Take, for instance, angular/flex-layout, which declares its RxJS dependency in this manner: "requiredAngularVersion": ">= ...

The instance is referencing property or method "foo" during render, but it has not been defined. To ensure reactivity, please make sure that this property is defined

Just starting out with the Vue framework and experimenting with component composition. However, I'm encountering an issue when trying to run the code: "Property or method "icons" is not defined on the instance but referenced during render. Make sure ...

Error TS2403: All variable declarations following the initial declaration must be of the same type in a React project

While developing my application using Reactjs, I encountered an error upon running it. The error message states: Subsequent variable declarations must have the same type. Variable 'WebGL2RenderingContext' must be of type '{ new (): WebGL2 ...

What is the best way to show an alert() when a user clicks on a marker in Google Maps?

My current setup:view image description ... google.maps.event.addListener(marker,'click',function() { this.map.setZoom(15); this.map.setCenter(marker.getPosition()); console.log('hello world'); this.presentAlert(); // ERROR co ...

Having difficulty transferring data between components using @Input syntax

I am having trouble passing the FailedProductId from Component1 to Component2 using @Input. Below is the code snippet: export class Component1 implements OnInit { public FailedProductId="produt"; constructor(private employeeService: ProductService) {} ...

Discovering ways to optimize argument type declarations in TypeScript

If we consider having code structured like this: function updateById( collection: Record<string, any>[], id: number, patch: Record<string, any> ): any[] { return collection.map(item => { if (item.id === id) { return { ...

PNG file is not displayed on React TSX page despite absence of any errors

I've implemented this initial design template from GitHub (https://github.com/vikpe/react-webpack-typescript-starter). Additionally, I'm utilizing react-bootstrap and have a container that includes a backgroundImage. const heroImage = require(&q ...

Issue with MUI Dialog: Struggling to pass the onClose function to onClick within dialog actions

Currently, I am in the process of developing a reusable component called Dialog which is based on MUI dialog. Below is the code snippet for this component: import React from 'react'; import { Dialog as MuiDialog, DialogProps, Button, Dia ...

AOT throws a `TypeError: Attempting to assign a value to a read-only property`, exclusively on AOT

An older project of mine was originally created in Angular 7. Recently, I felt inspired and decided to update it to version 13. During development, everything seemed to work fine. However, when I attempted to compile the project using ng serve to deploy it ...

An object that appears to be empty at first glance, but contains values that are undefined

I am facing an issue with my object that I am populating with information. The logs show the object as empty, but when I inspect it in Chrome, it appears to have the correct details filled in. Here is a snapshot of what the logs display: closed: closed o ...

The array used within the useEffect hook and the getCoordinates function appears to be distinct when printed with console

Utilizing GoogleMap API for Custom Location Display I have an imported array of JSON objects named data which includes an address property. The Google Maps API is used to retrieve coordinates from the addresses in order to generate custom markers displaye ...

Using the useContext hook in a TypeScript class component: a step-by-step guide

I am working with a TypeScript class component and have successfully created a context that can be accessed globally. I am interested in learning how to implement this context in a .ts class component and if it is possible to use it in a pure TypeScript ...

Guide on creating a custom type for an object utilizing an enum framework

Enumerating my shortcuts: export enum Hotkey { MARK_IN = 'markIn', MARK_OUT = 'markOut', GO_TO_MARK_IN = 'goToMarkIn', GO_TO_MARK_OUT = 'goToMarkOut' } I am now looking to define a type for a JSON ob ...