Associating function parameters with object types in TypeScript

In the conclusion of this post, I provide operational code for associating object types with a function that accepts an object containing matching properties. The code snippet I shared results in 'result' being resolved as:

type result = {
    GENERATION_COMPLETE: (params: {
        generation: ImageGeneration;
    }) => void;
    IMAGE_VOTE: (params: {
        voteForId: string;
    }) => void;
    SCORE_SCREEN_DONE: () => void;
    SUBMIT_THEME: (params: {
        theme: string;
    }) => void;
    THEME_VOTE: (params: {
        voteForId: string;
    }) => void;
}

However, my intention is to directly map to function parameters rather than an object with properties as a parameter to the function. As a result, the desired expansion of 'result' would look like this:

type result = {
    GENERATION_COMPLETE: (
        generation: ImageGeneration;
    ) => void;
    IMAGE_VOTE: (
        voteForId: string;
    ) => void;
    SCORE_SCREEN_DONE: () => void;
    SUBMIT_THEME: (
        theme: string;
    ) => void;
    THEME_VOTE: (
        voteForId: string;
    ) => void;
}

Type definition code:

type DreamEventSchema =
    | { type: 'GENERATION_COMPLETE', generation: ImageGeneration }
    | { type: 'IMAGE_VOTE', voteForId: string }
    | { type: 'SCORE_SCREEN_DONE' }
    | { type: 'SUBMIT_THEME', theme: string }
    | { type: 'THEME_VOTE', voteForId: string }

type OmitType<T> = Omit<T, 'type'> extends infer Omitted ? { [K in keyof Omitted]: Omitted[K] } : never;
type IsEmptyType<T> = keyof T extends never ? true : false;
type EventToFunction<Events extends { type: string }> = {
    [E in Events as E['type']]: (
        OmitType<E> extends infer Omitted
        ? IsEmptyType<Omitted> extends true
            ? () => void 
            : (params: Omitted) => void 
        : never
    )
}
type result = EventToFunction<DreamEventSchema>;

Do you have any suggestions on how to achieve this?

Answer №1

To enhance your input schema type, you can designate the labelled function parameter(s) as labelled elements of a tuple within a fixed property (e.g. params)...

type ImageGeneration = unknown;

type DreamEventSchema =
    | { type: "GENERATION_COMPLETE"; params: [generation: ImageGeneration] }
    | { type: "IMAGE_VOTE"; params: [voteForId: string] }
    | { type: "SCORE_SCREEN_DONE"; params: [] }
    | { type: "SUBMIT_THEME"; params: [theme: string] }
    | { type: "THEME_VOTE"; params: [voteForId: string] };

By using a mapped type, you can easily extract the necessary fields from the union members to derive the expected type:

type Transform<
  U extends Readonly<{ type: string; params: readonly unknown[] }>,
> = { [T in U as T["type"]]: (...params: T["params"]) => void };

type Expected = {
  GENERATION_COMPLETE: (generation: ImageGeneration) => void;
  IMAGE_VOTE: (voteForId: string) => void;
  SCORE_SCREEN_DONE: () => void;
  SUBMIT_THEME: (theme: string) => void;
  THEME_VOTE: (voteForId: string) => void;
};

type Actual = Transform<DreamEventSchema>;
/*   ^?
type Actual = {
  GENERATION_COMPLETE: (generation: unknown) => void;
  IMAGE_VOTE: (voteForId: string) => void;
  SCORE_SCREEN_DONE: () => void;
  SUBMIT_THEME: (theme: string) => void;
  THEME_VOTE: (voteForId: string) => void;
} */

// https://stackoverflow.com/a/53808212/438273
type Equal<A, B> =
  (<T>() => T extends A ? 1 : 2) extends
  (<T>() => T extends B ? 1 : 2) ? true : false;

declare const equal: Equal<Actual, Expected>;
            //^? const equal: true

Code in TS 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

Creating a custom React hook in TypeScript to handle mouse events

I have been working on creating a custom hook in TypeScript/React, and I am looking to convert the code snippet below into a custom hook. Currently, I am passing handleClick to the onClick attribute in a div element to detect user clicks and route them to ...

Issue encountered while deploying Next.js application on vercel using the replaceAll function

Encountering an error during deployment of a next.js app to Vercel, although local builds are functioning normally. The issue seems to be related to the [replaceAll][1] function The error message received is as follows: Error occurred prerendering page &q ...

Challenges with variable scopes and passing variables in Ionic 2 (Typescript)

In my Ionic 2 TypeScript file, I am facing an issue with setting the value of a variable from another method. When I close the modal, I get undefined as the value. I'm encountering difficulty in setting the value for coord. export class RegisterMapP ...

Angular is a powerful framework that enables the creation of dynamic user interfaces. One of its many

Looking to implement a Material table with expandable rows in Angular. table-tree.html <table mat-table [dataSource]="dataSource" multiTemplateDataRows class="mat-elevation-z8" > <ng-container matColumnDef="{{co ...

Tips for successfully sending an array of numbers using TypeScript and React

Exploring Types in React is new to me and I'm still navigating my way through it. My current challenge involves adding numbers from a form within a child component to an existing array of numbers. To tackle this, I've initialized a useState hoo ...

Can you explain the meaning of the code snippet: ` <TFunction extends Function>(target: TFunction) => TFunction | void`?

As I delve into the contents of the lib.d.ts file, one particular section caught my attention: declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void; The syntax in this snippet is a bit perplexing to m ...

Utilizing a setup module for configuration purposes

In the process of developing my angular application, I have integrated several external modules to enhance its functionality. One crucial aspect of my final application is the configuration classes that store important values like URLs and message keys us ...

Tips for invoking a function from one React component to another component

Currently, I am working on two components: one is Game and the other is PickWinner. The Game component serves as the parent component, from which I need to call the pickWinner function in the PickWinner component. Specifically, I want to trigger the startP ...

Importing CSS properties from MUI v5 - A comprehensive guide

I'm working with several components that receive styles as props, such as: import { CSSProperties } from '@material-ui/styles/withStyles' // using mui v4 import because unsure how to import from v5 paths import { styled } from '@mui/mat ...

Guide to simulating Twilio with Jest and TypeScript to perform unit testing

Please assist me in mocking a Twilio service that sends messages using Jest to mock the service. Below is the code I am working with: import { SQSEvent } from "aws-lambda"; import { GetSecretValueResponse } from "aws-sdk/clients/secretsmanag ...

Is there a gap in the Nativescript lifecycle with the onPause/onResume events missing? Should I be halting subscriptions when a page is navigated

My experience with NativeScript (currently using Angular on Android) has left me feeling like I might be overlooking something important. Whenever I navigate to a new route, I set up Observable Subscriptions to monitor data changes, navigation changes, an ...

The element class type 'xxx' is lacking several properties compared to the 'ElementClass' type, including context, setState, forceUpdate, props, and others. This issue is flagged with error code TS2605

Encountering an error while attempting to execute my react app: D:/React/my-components/src/App.tsx TypeScript error in D:/React/my-components/src/App.tsx(23,4): JSX element type 'Confirm' is not a constructor function for JSX elements. ...

What types of modifications do ViewChildren and ContentChildren QueryLists keep an eye out for?

Imagine you come across the following lines of code: https://i.stack.imgur.com/7IFx1.png And then, in a different section, you stumble upon this code block: https://i.stack.imgur.com/qac0F.png Under what circumstances would () => {} be executed? Wha ...

React Native bottom tab navigator not changing between tabs

Hi, I'm new to React Native and I think I might have a structural issue because I can't figure out what I'm doing wrong. I'm trying to create 4 tabs, but when I click on each tab, it doesn't take me to the next page. Nothing happe ...

Turn off the touch events system for Ionic 2 on the leaflet map's draw controller

Seeking guidance on how to disable data-tap functionality in Ionic 2 within a leaflet map div. Anyone familiar with this? In Ionic-v1, the solution involved adding data-tap-disabled="true" to the map container (ion-content). I recently integrated the lea ...

Step-by-step guide for adding an object to a Material UI select component

I am in the process of incorporating a Select component with reactjs material ui and typescript. Yet, I encounter this typing error: Property 'id' does not exist on type 'string'. Property 'name' does not exist on type ' ...

Directive for masking input values

I am in need of an input that adheres to the following format: [00-23]:[00-59] Due to the limitations of Angular 2.4, where the pattern directive is unavailable and external libraries like primeNG cannot be used, I have been attempting to create a direct ...

Is there a potential issue in Next.js 14 when utilizing the "useClient" function alongside conditional rendering in the app/layout.tsx file?

Within my app, there is a Navbar that will only be visible when the route is either "/" or "/teachers". The Navbar will not appear on the dashboard page ("/dashboard"). I achieved this using conditional rendering in the app/layout.tsx file. "use clien ...

Interactive MUI React Tab Components

Currently, I am working with MUI tabs and have added an X button to them. However, I am facing difficulties in making the tabs closeable. I would greatly appreciate any help or guidance on how to achieve this feature. Despite trying different methods, I ha ...

Convert the generic primitive type to a string

Hello, I am trying to create a function that can determine the primitive type of an array. However, I am facing an issue and haven't been able to find a solution that fits my problem. Below is the function I have written: export function isGenericType ...