Mapped Generics in Typescript allows you to manipulate and

Currently, I am attempting to utilize TypeScript generics in order to transform them into a new object structure. Essentially, my goal is to change:

{ 
        key: { handler: () => string },
        key2: { hander: () => number },
    }
    

to:

{ key: string, key2: number }
    

Here's a complete example:

type programOption = {
        validator: () => unknown
    }

    type programConfig<T extends {[key: string]: programOption} = {}> = {
        options: T,
        handler: (data: mapProgramConfig<T>) => void,
    }

    type mapProgramConfig<T extends {[key: string]: programOption}> = {
        [K in keyof T]: ReturnType<programOption['validator']>
    }

    type mapProgramConfigHardcoded<T> = {
        fruit: string,
        animal: number
    }

    class Program {
        constructor (config: programConfig) {}
    }

    const foo = new Program({
        options: {
            'fruit': { validator: () => 'asdf' },
            'animal': { validator: () => 42 },
        },
        handler: ({fruit, animal, thing}) => {

        },
    });
    

If you replace mapProgramConfig with mapProgramConfigHardcoded in the programConfig type, you can see what I'm trying to achieve. However, I am struggling to make it work in the generic scenario.

ts playground link

Answer №1

Take a look at this proposed solution:

type ProgramOption<T> = {
  validator?: () => T
}

type Convert<Obj extends Record<string, ProgramOption<any>>> = {
  [Prop in keyof Obj]: Obj[Prop]['validator'] extends () => infer Return ? Return : never
}
const program = <
  Keys extends PropertyKey,
  ValidatorValues extends string | number,
  Options extends Record<Keys, ProgramOption<ValidatorValues>>,
  Handler extends (data: Convert<Options>) => void,
  >(data: { options: Options, handler: Handler },) => {
  return data
}

const foo = program({
  options: {
    'fruit': { validator: () => 'string' },
    'animal': { validator: () => 42 },
  },
  handler: (obj) => {
    obj.animal // 42
    obj.fruit // 'string'
  }
});

article

I opted for the program function instead of a Program class due to the fact that

Type parameters cannot appear on a constructor declaration

Answer №2

To define it, you can use the following structure:

const configuration<T extends Record<string, any>> = {
  options: {
    [K in keyof T]: {
      validator: () => T[K]
    }
  },
  handler: (data: T) => void,
}

class Settings<T extends Record<string, any> = Record<string, any>> {
  constructor(config: configuration<T>) { }
}

When calling it, the generic will be inferred based on the necessary data, including key and data type.

const exampleConfig: Settings<{
    fruit: string;
    animal: number;
}>

TS Playground

Answer №3

To implement the programConfig interface into the Program class, you simply need to extend the generic specified:

class Program<T extends {[key: string]: programOption} = {}> {
  constructor (config: programConfig<T>) {}
}

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

The parameter 'NextApiRequest' cannot be assigned to the parameter 'Request'

I encountered a typescript issue that states: Argument of type 'NextApiRequest' is not assignable to parameter of type 'Request'. Type 'NextApiRequest' is not assignable to type '{ url: string; }'. Types of pro ...

Function type guards in Typescript do not support type inference

When checking for null in alpha, I validate the result and use throw new Error if needed. However, even after doing so, the compiler still indicates a compilation error: const obj = { objMethod: function (): string | null { return 'always a str ...

Issues with navigation in React Native Typescript

Currently, I am in the process of developing a new React Native Expo project utilizing TypeScript. I have been attempting to configure navigation following the guidance provided in React Native's TypeScript documentation. However, upon running and sim ...

Display a React component according to the user's input

Within the first (parent) div, there is an underlined message stating: "This JSX tag's 'children' prop expects a single child of type 'ReactNode', but multiple children were provided.ts(2746)". import A from './components/A&ap ...

Having trouble getting the styles property to work in the component metadata in Angular 2?

Exploring Angular 2 and working on a demo app where I'm trying to apply the styles property within the component metadata to customize all labels in contact.component.html. I attempted to implement styles: ['label { font-weight: bold;color:red } ...

Depend on a mapping function to assign a value to every option within a discriminated union

While utilizing all variations of a discriminated union with conditional if statements in TypeScript, the type is narrowed down to the specific variant. To achieve the same effect by expressing the logic through a mapping from the discriminant to a funct ...

Pause and anticipate the occurrence of AdMob's complimentary video reward event within a defined function in Ionic/Typescript

In my ionic app, I have a function that determines if the user has watched a reward video to access another function: let canUseThisFunction = await this.someService.canUseFunction(); if(canUseThisFunction){ console.log("can use"); } else { cons ...

Tips for correctly passing the appropriate data type using generics in TypeScript

If I have an array of objects with a key called render, which may be an optional function that takes a parameter (unknown type) const objectArray = [{a: 1}, {b: 2, render: renderFunction}, {c: 3, render: anotherFunction}] suppose the second object's ...

React Material-UI - implementing custom colors in Alert component leads to error: "Cannot read properties of undefined (reading 'type')"

I've been working on customizing MUI components to match our company's design, but I've hit a roadblock. While defining my custom colors, I noticed that instead of "error" we have a color called "danger." I followed the guidelines in the do ...

Angular is encountering a circular dependency while trying to access a property called 'lineno' that does not actually exist within the module exports

I am working on an Angular project and using the Vex template. My project utilizes Angular 9 and Node.js v15.2.0. Every time I run the project with the command ng serve -o, it displays a warning message. https://i.stack.imgur.com/8O9c1.png What could b ...

The android() method could not be located on the root project 'xyz' of type org.gradle.api.Project when passing [before_plugins_] as arguments

Encountering an issue while attempting to run the Nativescript Android application on my device. The error message states: Could not find method android() for arguments [before_plugins_d5qrcua3za3scd760qug60fz6$_run_closure1@5754ca71] on root project &apos ...

Tips for implementing assertions within the syntax of destructuring?

How can I implement type assertion in destructuring with Typescript? type StringOrNumber = string | number const obj = { foo: 123 as StringOrNumber } const { foo } = obj I've been struggling to find a simple way to apply the number type assertio ...

Next.js encountered an error when trying to locate the 'net' module while working with PostgreSQL

I'm facing a challenge in my Next.js project while attempting to retrieve all records from a table. The error message I'm encountering is "Module not found: Can't resolve 'net'" with an import trace pointing to multiple files withi ...

Using Vue.js 3 and Bootstrap 5 to Create a Custom Reusable Modal Component for Programmatically Showing Content

Trying to develop a reusable Modal Component using Bootstrap 5, Vuejs 3, and composible API. I have managed to achieve partial functionality, Provided (Basic Bootstrap 5 modal with classes added based on the 'show' prop, and slots in the body a ...

Type guards do not work properly on a union of enum types in TypeScript

Recently delved into the concept of Type Guards Chapter within the realm of Typescript However, I encountered an issue where my basic type guards failed to differentiate a union of enums. Why is this happening? enum A { COMMA = ',', PLUS = & ...

Session is not functioning properly as anticipated

import * as express from 'express'; import * as session from 'express-session'; import * as bodyParser from 'body-parser'; const app: express.Express = express(); app.use(bodyParser.json()); app.use(session({ secret: &apos ...

Mastering server requests in Angular 5

I have come across recommendations stating that server requests should be made via services and not components in order to ensure reusability of functions by other components. Ultimately, the server response is needed in the component. My query pertains t ...

By constantly subscribing to a Behavior Subject, updating the data, and then resetting the value, an endless loop is created

We have implemented a wizard functionality with lazy loading, consisting of a parent component and multiple child components. const routes: Routes = [ { path : '', component : WizardHomeComponent, canActivate: [HomeGuard], chil ...

Injection of environmental variables into app services

Through the use of Nx, I have created multiple apps that each have their own environment with different API URLs. The Nx Workspace library includes shared services that are utilized among all apps, however, it is necessary to pass the environment-api-url w ...

Organizing Firebase functions: Managing multiple functions and dependencies

Objective I aim to gain a deeper understanding of how the firebase CLI manages the deployment process for multiple firebase functions, each stored in different files, and how it handles dependencies that are specific to certain functions. Situation If I ...