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

Troubleshooting data binding problems when using an Array of Objects in MatTableDataSource within Angular

I am encountering an issue when trying to bind an array of objects data to a MatTableDataSource; the table displays empty results. I suspect there is a minor problem with data binding in my code snippet below. endPointsDataSource; endPointsLength; endP ...

What is the best way to enable code sharing between two TypeScript projects housed within the same repository?

Our project has the following structure: api (dir) -- package.json -- tsconfig.json -- src (dir) -- client (dir) ---- package.json ---- tsconfig.json ---- src (dir) The "client" directory houses a create-react-app project that proxies to the API d ...

What is the best way to show/hide group items in a PrimeNG dropdown menu?

Is it possible to show or hide group items when clicking on the group header if the group contains items? For instance, I would like to display 3 items (AA, BB, CC) in a dropdown menu. The first 2 options (AA and BB) should be selectable, but when I click ...

Develop a TypeScript class that includes only a single calculated attribute

Is it advisable to create a class solely for one computed property as a key in order to manage the JSON response? I am faced with an issue where I need to create a blog post. There are 3 variations to choose from: A) Blog Post EN B) Blog Post GER C) Bl ...

Use JavaScript's Array.filter method to efficiently filter out duplicates without causing any UI slowdown

In a unique case I'm dealing with, certain validation logic needs to occur in the UI for specific business reasons[...]. The array could potentially contain anywhere from several tens to hundreds of thousands of items (1-400K). This frontend operation ...

Preventing image flickering in SvelteKit: A guide

Upon the initial loading of a website, you may notice that the images tend to flicker or flash when transitioning between them. However, once these images are stored in the browser's cache, subsequent visits to the site will display the images seamles ...

Issue: Typescript/React module does not have any exported components

I'm currently facing an issue with exporting prop types from one view component to another container component and using them as state type definitions: // ./Component.tsx export type Props { someProp: string; } export const Component = (props: ...

Unable to locate the module '@vitejs/plugin-react' or its associated type

After creating the project with npm create vite@latest and selecting ts-react, everything seemed to work fine when I ran npm run dev. However, in my vs code editor, I encountered the error message "Cannot find module '@vitejs/plugin-react' or its ...

What is the best practice for Angular: running production build before or after testing?

When developing a Java application for production, I typically set up the build process to create the production artifacts first and then run tests against those artifacts. Recently, I joined an Angular project and noticed that the build process is struct ...

Can we modify the auto-import format from `~/directory/file` to `@/directory/file`?

I have a small issue that's been bugging me. I'm working on a project using Nuxt3 and Vscode. When something is auto-imported, Vscode uses the ~/directory/file prefix instead of the preferred @/directory/file. Is there an easy way to configure Vs ...

What is the reason for VS Code not displaying doc comments from type properties when suggesting descriptions and hover info for TypeScript objects?

The problem is clearly visible in the image below, as it pertains to the popup box in VSCode displaying type information and comments. Although the code works fine in VSCode, TypeScript Playground fails to display the comments properly, so it's best ...

The name 'Diagnostics' cannot be located

I've downloaded the Typescript repository and am currently reviewing the code. However, I keep encountering this recurring error message: Cannot find name 'Diagnostics' This error pops up on lines that are similar to this: Diagnostics._ ...

Is it possible to enable password authentication on Firebase even if the user is currently using passwordless sign-on?

In my frontend JS project, I have integrated Firebase for web and am utilizing the passwordless (email link) authentication method for users. I am now interested in implementing password sign-on for an existing user who is currently using passwordless si ...

Error in Angular 5: Attempting to access 'subscribe' property of undefined variable

I've been struggling for days trying to fix this error on Stack Overflow. Since I'm new to Angular, I decided to reach out to the community for help. The issue revolves around JWT authentication. ERROR TypeError: Cannot read property 'sub ...

Error occurs because the declaration for `exports` is missing in the compiled TypeScript code

I am currently venturing into the world of typescript and I've encountered a problem while attempting to run my application. An error is popping up, stating ReferenceError: exports is not defined The code I have written is rather straightforward: / ...

How to convert form fields into JSON format using Angular 2

Currently, I am in the process of learning angular2 and have encountered a roadblock. I have created a form where the values are populated through JSON. The form consists of both pre-filled fields and text input fields where users can enter data and select ...

Workspace watch mode does not update Typescript definitions

Greetings to all who are reading this, I have created a React micro-frontend project using SPA v5.9, Webpack v5.75, Babel-loader v9.1, Ts-loader v9.4, and Yarn workspace v3.5. The project structure is very basic: Root SPA => Package Root Application S ...

When using Vue with Vuetify, be aware that object literals can only specify known properties. In this case, the type 'vuetify' does not exist in the ComponentOptions of Vue with DefaultData

Started a fresh Vue project with TypeScript by following this guide: https://v2.vuejs.org/v2/guide/typescript.html If you don't have it installed yet, install Vue CLI using: npm install --global @vue/cli Create a new project and choose the "Manual ...

Strategies for effectively choosing this specific entity from the repository

Is it possible to choose the right entity when crafting a repository method using typeorm? I'm facing an issue where I need to select the password property specifically from the Admin entity, however, the "this" keyword selects the Repository instead ...

Creating a TypeScript library with Webpack without bundling may seem like a daunting task, but

Currently, I am developing a React component package using Typescript and NPM. During my research, I discovered that generating individual .js and .d.ts files is more beneficial than bundling them into one bundle.js file. However, I am facing difficulty in ...