TS Mapped Type: Dynamically exclude specific keys based on their values

Seeking a method to create a mapped type that excludes specific keys based on the value of the mapped key.

For instance:

Consider an option: Options struct, where Options is a union type defined as:

{ type: DataType } or { type: DataType, params: DataParam }

Within this union, some elements have a key named params, while others do not. The goal is to construct such a union utilizing a mapped type. Here's a functioning solution (Playground Link):

type DataParams = {
  trade: never;
  trade_bar: {
    interval: number;
    intervalUnit: "ms" | "s" | "m" | "ticks" | "vol";
  };
};


type DataTypes = keyof DataParams;

type OptionsForDataType<T extends DataTypes> = {
  type: T;
} & (DataParams[T] extends never
  ? {}
  : {
      params: DataParams[T];
    })


type Options = {
  [DataType in DataTypes]: OptionsForDataType<DataType>;
}[DataTypes];

However, I find the approach using intersection + extends to be somewhat unsatisfactory. Is there a more elegant way to achieve the same outcome?

I had mistakenly assumed that when { key: never } is used, the respective key is effectively excluded from the structure since nothing can be assigned to a key with the type never. Surprisingly, this turned out not to be the case, which seems peculiar to me.

Answer №1

It's a bit embarrassing that I didn't thoroughly research through Google Diligence™️. When I searched for the keywords "mapped type exclude never value," it directed me to the microsoft/TypeScript#23199 thread, where others had expressed the same confusion and found a solution.

The essence of that thread is that my uncertainty was justified, as the TS team also believes that { key: never } should exclude the key. However, due to technical challenges, they have been unable to implement this directly.

The recommended workaround is to create a utility type that filters out the desired keys and apply it as an intermediary step. The solution provided in the thread, tailored to my specific case, looks something like this:

// Utilities:
type FilteredKeys<T> = { [P in keyof T]: T[P] extends never ? never : P }[keyof T];
type ExcludeNeverFields<O> = {
  [K in FilteredKeys<O>]: O[K]
}

// Use case:
type DataParams = {
  trade: never;
  trade_bar: {
    interval: number;
    intervalUnit: "ms" | "s" | "m" | "ticks" | "vol";
  };
};

type DataTypes = keyof DataParams;

type Options = {
  [DataType in DataTypes]: ExcludeNeverFields<{
    type: DataType;
    params: DataParams[DataType];
  }>
}[DataTypes];

Answer №2

In the latest version of TypeScript (4.1), a new feature called Key Remapping allows you to selectively remove keys based on a conditional type.

To remove a specific key, you need to use a conditional type that determines whether to exclude it by returning never.

type RemoveCertainKey<T> = {
  [K in keyof T as CONDITION]: T[K]
}

// The CONDITION is a conditional type that can decide to keep, modify, or exclude the property

Here's a playground link where you can experiment with this functionality. This approach allows for dynamic exclusion or modification of properties based either on their keys or values.

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

Having trouble debugging TypeScript files in Chrome following an Angular update

I have been experimenting with writing a basic app using Angular 4.2.5 to expand my knowledge. Previously, I was able to debug the TypeScript files in Chrome without any issues. However, after updating to Angular 5.2.7, I am no longer able to view the Type ...

Delay the Ngrx effect by 2 seconds before initiating the redirect

I have an ngrx effect that involves calling an HTTP method and then waiting for 2 seconds before redirecting to another page. However, the current behavior is that it redirects immediately without waiting. confirmRegistration$ = createEffect(() => { ...

Customizing TinyMCE's font style menu options

Our platform utilizes TinyMCE as in-place editors to allow users to make live edits to content. However, a challenge arises when using a dark background with light text, as TinyMCE defaults to using this text color rather than black. https://i.sstatic.net ...

Navigating to specific rows in a primeng virtualscroll table using the scrollToIndex

Utilizing Primeng virtual scroll table in Angular to manage large datasets in an array. I am interested in using the scrollToIndex. Is there an equivalent of cdk-virtual-scroll-viewport in Primeng table? I need the functionality where, upon a user clicking ...

Mastering the art of connecting content within Prismic

I have been working on creating a mega menu for my website header, but I am encountering a type error. Has anyone else faced this issue before and how did you resolve it? I am currently importing the generated types from @/prismicio-types. Here is an exam ...

Utilizing Vue and TypeScript - Retrieving a Variable Declared in the Data() Method within Another Method

I recently made the switch to using Vue3 with TypeScript after previously working with Vue2 and JavaScript. I am encountering an issue in my IDE where it is showing an error (even though the code itself functions correctly, but may not be entirely accurate ...

Issue with the declaration of custom types in Typescript

I've created a type declaration for r-dom as shown below: /// <reference types="react" /> declare module 'r-dom' { interface IRDOMFacade extends React.ReactDOM { (component: React.Component<any, any>, properties?: ...

Is your Angular5 service failing to transmit data?

I have two components in my code, A and B. Component A contains a form with data that I want to send to component B. However, it seems like component B is not receiving any data. Here is the code for Component A: import { MyService } from 'path/my ...

Is it possible to implement typed metaprogramming in TypeScript?

I am in the process of developing a function that takes multiple keys and values as input and should return an object with those keys and their corresponding values. The value types should match the ones provided when calling the function. Currently, the ...

Tips for accessing the StaticRouterContext in Typescript with react-router-dom

Currently, I am implementing SSR for my app specifically targeting robots. There is a possibility that the render of the <App/> component may lead to a route not being found. In order to handle this scenario, I need to identify when the render ends ...

Unable to loop through using ngFor

I have a component that retrieves data from the back-end and groups it accordingly. Below is the code snippet: getRecruitmentAgencyClientPositions(): void { this._recruitmentAgencyClientsService.getRecruitmentAgencyClientPositions(this.recruitmentAge ...

HTML code for adding a dropdown menu inside a table cell

I am trying to set up a table where one cell functions as a dropdown menu. The data.field is being fetched from the backend and I want it to be displayed during rendering. Additionally, a fList is also retrieved from the backend. When a user clicks on th ...

TypeScript: displaying parameters

variable_field = [["S",".","."],[".","#","."],[".",".","T"]]; <ng-template ngFor let-array [ngForOf]="field_array" let-x="index"> <ng-t ...

How can I retrieve routing parameters in a Vue.js/Nuxt/TypeScript app?

In the process of developing my website based on the Nuxt TypeScript Starter template, I've encountered a challenge. Specifically, I have created a dynamically routed page named _id.vue within my pages folder and am looking to access the id property i ...

Immer along with a subclass of Map

Recently, I decided to implement useImmerReducer in my React application for the first time, but encountered an issue. In my project, I utilize a custom class called Mappable, which is derived from Map: import { immerable } from "immer"; type K ...

Challenges with image cropping in Angular causing performance problems

Utilizing this specific component for image cropping within an ionic/angular8 project has been causing severe performance issues, leading to unresponsiveness on mobile devices. Interestingly, the desktop version does not encounter any problems and the crop ...

Stop any modifications to the input data

We are currently in the process of building an angular application with Angular 5. Typically, our components have input and output parameters defined as shown below: export class MultipleItemPickerComponent implements OnInit { @Input() itemPickerHeader ...

Issue with React Redux: Store dispatch not causing component update

I have recently implemented React Redux in my project, but I seem to be encountering some issues. Despite changing the state, the value remains the same. I attempted to use useStore(), but it does not take any parameters. Can anyone provide insight into wh ...

The `note` binding element is assumed to have an unspecified `any` type

I'm encountering an error that I believe is related to TypeScript. The issue arises when trying to work with the following example. I am using a JavaScript server to import some notes. In the NoteCard.tsx file, there is a red line under the {note} cau ...

Unknown error occurred in Eventstore: Unable to identify the BadRequest issue

I'm encountering an error while using Eventstore, specifically: Could not recognize BadRequest; The error message is originating from: game process tick failed UnknownError: Could not recognize BadRequest at unpackToCommandError (\node_modul ...