Create a fresh type by dynamically adjusting/filtering its attributes

Suppose we have a type defined as follows:

type PromiseFunc = () => Promise<unknown>;
type A = {
  key1: string;
  key2: string;
  key3: PromiseFunc;
  key4: string;
  key5: PromiseFunc;
  key6: SomeOtherType1[];
  key7: SomeOtherType2[];
  key8: string;
  key9: PromiseFunc;
  // ...
};

Given the type structure above, my goal is to define a new type, B, with the following characteristics:

  • All keys in A that are of type string should appear as type string in B
  • All keys in A that are of type PromiseFunc should appear as type string in B
  • Any keys in A that are arrays should be excluded from B

Is there a generic method to achieve this transformation? I have numerous types similar to A and I'm looking for a way to convert them into types like B. Keep in mind that I do not have control over the generation of the A types.

Edit: Additional solution available at: https://www.typescriptlang.org/play?#code/C4TwDgpgBAIghsCAFATgewLYEsDO0C8UAFAJRT4B8UqmuEAPPIhQNwBQokUA4hAHYQUWAMY1sectXTiGOYEL4BzCmzYB6NVAAq4aMDRQAZlgA2iFB12wEcSQG82UKAGsIIAIwAuKHIWL2Tq4gAEzeTMjSdI4ubgDM3nB8IADaALrRQQAs3rwCQqKReBluAKzepORUYnT0fACuGABGgioAvuyWXACycGCouBhwOpBaaADKJmh8WM70APKNAFZQEAAeiHwAJjhQAEoQwmgom-S+WEoANFCJIBRXPcDCABYAanAmdRDDEFf7OHVmN4fL66KiEBxOZIAaSg5xiIDQhmoIlm0ScC0WFzRUAhTjx0LcsL48MRUAxqW8GIJIFSK3W-G2UAezyBn2+UAA-FAoYTvAIAG6CAJ41rJIKk8nRCgUvYQf6A95s3TsdqqTjQfqDap4UYTKYzb4AYSmgpQ5noWjBTN6-Rwg2+usm01mWiu4W1Pyg9SaLSgADJrX0hHahrpHfqXVdcoIRB6oAAfHzyc6KK5nJQqNiHPhyHyYCCsiDeTVwD3h51Gk2Cc1MOBW3FBLxQADkhjQaGbWMCblCUHcXfh2RbWE2mU7bHaQA

Answer №1

After some experimentation, I have found a solution to my problem. Although there may be more efficient ways to handle these type conversions, the current method is functional:

type PromiseFunc = () => Promise<unknown>;

// Define types to exclude certain keys.
type SomeOtherType1 = {
    key1: string[];
};

type SomeOtherType2 = {
    key1: number;
};

type A = {
    key1: string;
    key2: string;
    key3: PromiseFunc;
    key4: string;
    key5: PromiseFunc;
    key6: SomeOtherType1[];
    key7: SomeOtherType2[];
    key8: string;
    key9: PromiseFunc;
};

// Create a filter type to handle specified conditions and convert other types.
type Filter<T, ConditionOne, ConditionTwo> = {
    [Key in keyof T]: T[Key] extends ConditionOne
        ? string
        : T[Key] extends ConditionTwo
        ? string
        : number;
};

// Convert to a new type with only string and number types.
type ConvertedType = Filter<A, string, PromiseFunc>;

// Drop keys that do not match the desired type (string).
type PickByValueType<T, U> = {
    [K in keyof T as K extends K ? (T[K] extends U ? K : never) : never]: T[K];
};

// Generate final type with only string keys.
type FilteredType = PickByValueType<ConvertedType, string>;

The primary issue I faced was when using never instead of number, which did not effectively exclude unwanted keys. This discrepancy was clarified through an investigation into TypeScript's behavior, particularly in version 4.1.2. While further optimization may involve combining the Filter and PickByValueType types, the current implementation suffices.

In case future users encounter similar challenges, employing two type conversions proves essential for accommodating multiple types. To include various types, a flexible approach can be applied:

...

type GrabTypes<T, U, U2> = {
    [K in keyof T as K extends K
        ? T[K] extends U
            ? K
            : T[K] extends U2
            ? K
            : never
        : never]: T[K];
};

type GrabbedTypes = GrabTypes<A, string, SomeOtherType1[]>

Despite existing resources on utilizing omit for excluding specific types, examples addressing the exclusion of multiple keys with diverse types remain scarce. Extending this approach to incorporate conversion could enhance its versatility within the confines of ternary operators:

...

type GrabTypesAndConvertOne<T, U, TypeToConvert> = {
    [K in keyof T as K extends K
        ? T[K] extends U
            ? K
            : T[K] extends TypeToConvert
            ? K // <- Implement type change logic here
            : never
        : never]: T[K];
};

type GrabbedTypes = GrabTypesAndConvertOne<A, string, PromiseFunc>

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 importing the Renderer2 component in Angular

Trying to include Renderer2 with the following import: import { Renderer2 } from '@angular/core'; Encountering an error: "Module 'project/node_modules/@angular/core/index' does not have an exported member 'Renderer2'. Puzz ...

Exporting the default value from a TypeScript declaration file module

Imagine having a declaration file called foo.d.ts: declare namespace foo { interface Bar { (): void; } } declare var foo: foo.Bar; export default foo; Upon compilation: import Foo from './foo'; Foo(); The result is: "use strict"; va ...

Creating reducers for a unique data type: A step-by-step guide

Today, I was working on enhancing a shopping website using React Typescript and Context API. My goal is to utilize React Reducers to manage the state of my Shopping Cart. I have custom Types defined for the Product type, which includes an Items Array and s ...

WebStorm lacks support for TypeScript's `enum` and `readonly` features

When working with TypeScript in WebStorm, I encountered an issue where the IDE does not recognize enum or readonly. To solve this problem, I delved into TypeScript configuration files. I am currently utilizing .eslintignore, .eslintrc, tsconfig.json, and ...

Enhancing Hapi.js server functions with TypeScript: A guide

One way to enhance the functionality of the hapi module by adding type checking for server methods is shown in the following code snippet: import { Server } from 'hapi'; declare module 'hapi' { export interface Server { m ...

When incorporating an array as a type in Typescript, leverage the keyof keyword for improved

I am facing a situation where I have multiple interfaces. These are: interface ColDef<Entity, Field extends keyof Entity> { field: Field; valueGetter(value: Entity[Field], entity: Entity): any } interface Options<Entity> { colDefs ...

Understanding the limitations of function overloading in Typescript

Many inquiries revolve around the workings of function overloading in Typescript, such as this discussion on Stack Overflow. However, one question that seems to be missing is 'why does it operate in this particular manner?' The current implementa ...

An error occurred suddenly while building: ReferenceError - the term "exports" is not defined in the ES module scope

Having trouble resolving this error in the Qwik framework while building a static site: ReferenceError: exports is not defined in ES module scope at file:///media/oem/MyFiles/8_DEVELOPMENT/nexasoft/server/@qwik-city-plan.mjs:1:1097 at ModuleJob. ...

"Exploring the TypeScript typing system with a focus on the typeof operator

My goal is to create a function that will return the typeof React Component, requiring it to adhere to a specific props interface. The function should return a type rather than an instance of that type. Consider the following: interface INameProps { ...

Encountering Next.js Hydration Issue when Using Shadcn Dialog Component

While working on a Next.js project, I came across a hydration error when utilizing the Shadcn Dialog component. The specific error message reads: "Hydration failed because the initial UI does not match what was rendered on the server." Highligh ...

Using TypeScript to call a class method from within another function

Currently, I am working on an Angular 2 application and struggling to grasp the concept of TypeScript's this scope. Within my TypeScript class named SharedService, there is a function called handleError. If this function encounters a 401 status, I wa ...

Limiting the height of a grid item in MaterialUI to be no taller than another grid item

How can I create a grid with 4 items where the fourth item is taller than the others, determining the overall height of the grid? Is it possible to limit the height of the fourth item (h4) to match the height of the first item (h1) so that h4 = Grid height ...

Restrict the number of subscriptions allowed for an rxjs observable

Seeking a replacement for observable, subject, or event emitter that allows only one subscription at a time. The first subscriber should have priority to execute its subscribe function, with subsequent subscribers waiting their turn until the first unsubsc ...

Steps for updating text within an object in Angular

details = [ { event: "02/01/2019 - [Juan] - D - [Leo]", point: 72 }, { event: "02/01/2019 - [Carlo] - N - [Trish]", point: 92 } ]; I am attempting to modify the text within the titles that contain - N - or - D - The desired outcom ...

Tips for incorporating attributes into a customized Material-UI props component using TypeScript in React

I'm interested in using material-ui with react and typescript. I want to pass properties to the components, but I'm having trouble figuring out how to do it. Currently, I'm working with the react-typescript example from the material-UI repos ...

Having difficulties viewing the sidemenu icon on Ionic 3, despite enabling the menu through MenuController

I am trying to show the sidemenu icon on my Home page, which users can access from the Add-Contract page. Even though I have enabled the sidemenu in home.ts using this.menu.enable(true);, the icon is not visible. However, I can still swipe and access the ...

What are some methods to troubleshoot $injector type errors in TypeScript?

I'm currently facing an issue with my AngularJS code. Here is a snippet of what I have: this.$injector.get('$state').current.name !== 'login' But, it's giving me the following error message: error TS2339: Property 'c ...

Obtaining Input Field Value in Angular Using Code

How can I pass input values to a function in order to trigger an alert? Check out the HTML code below: <div class="container p-5 "> <input #titleInput *ngIf="isClicked" type="text" class="col-4"><br& ...

Error 2322: Troubleshooting Typescript arrow functions overloads issues

Everything seems to be working well with the code below, except for an error that occurs with the resolve constant. const resolve: Resolve Type '(param: "case 1" | "case 2" | "case 3") => boolean | "string" | ...

Is the Cyrillic encoding feature not functioning properly in Angular 4 with .Net Core 2?

Struggling with handling Cyrillic characters in my current project. Utilizing .Net Core 2 along with Angular 4.2.5 I've noticed that displaying a string in the templates using {{ someCyrillicString }} works fine. However, encountering issues when tryi ...