What is a mapped Record type in which each key K in Record<T, K> is determined by the value of T?

My previous question from three weeks ago has led to this extension: Set the keys of an interface to the possible values of a different interface?

In summary, I have the following type definitions:

interface SuccessStatus {
  type: 'success';
  payload: string;
}

interface LoadingStatus {
  type: 'loading';
} 

interface ErrorStatus {
  type: 'error';
  error: string;
}

type RequestStatus = SuccessStatus | LoadingStatus | ErrorStatus;

And I have created a mapped Record type to define an object that handles each of the above statuses:

type RequestHandlerVisitor = Record<
  RequestStatus["type"], 
  (status: RequestStatus) => void
>;

With each T having a corresponding K function.

This results in an object like this:

const statusVisitor: RequestHandlerVisitor = {
  "success": (status: RequestStatus) => { ... },
  "loading": (status: RequestStatus) => { ... },
  "error": (status: RequestStatus) => { ... },
}

Now, I am looking to create a similar type where the value of K changes based on the key T, such as:

const statusVisitor: NewRequestHandlerVisitor = {
  "success": (status: SuccessStatus) => { ... },
  "loading": (status: LoadingStatus) => { ... },
  "error": (status: ErrorStatus) => { ... },
}

In this case, the first argument of function K will vary based on T.

One potential solution is to hardcode the type like this:

interface NewRequestHandlerVisitor {
  "success": (status: SuccessStatus) => void;
  "loading": (status: LoadingStatus) => void;
  "error": (status: ErrorStatus) => void;
}

While this works for now, it may become cumbersome when dealing with more "Status" types, as each would require a new entry in the type.

Is there a way to dynamically define this?

Thank you!

Answer №1

To achieve this functionality, one can utilize a personalized mapped type along with the Extract conditional type :

interface ResponseSuccess {
  status: 'success';
  data: string;
}

interface ResponseLoading {
  status: 'loading';
} 

interface ResponseError {
  status: 'error';
  errorMessage: string;
}

type ApiResponse = ResponseSuccess | ResponseLoading | ResponseError;

type ResponseHandlerVisitor = {
  [P in ApiResponse["status"]]: (response: Extract<ApiResponse, { status: P }>) => void
}

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 Vite proxy server will not modify POST requests

When I set up a proxy using Vite, I noticed that it only handles GET and HEAD requests. I'm looking to have other request methods proxied as well. In a new Vite React project - the only modification I made was in vite.config.ts import { defineConfig ...

Troubleshooting Issue with Mongoose Virtual Field Population

I am currently facing an issue with my database due to using an outdated backend wrapper (Parse Server). The problem arises when dealing with two collections, Users and Stores, where each user is associated with just one address. const user = { id: &q ...

Encountering a snag while setting up Google authentication on my website

Currently, I am in the process of integrating Google Authentication into my website. However, I have run into an error related to session management that reads as follows: TypeError: req.session.regenerate is not a function at SessionManager.logIn (C:&bso ...

Converting a nested JSON object into a specific structure using TypeScript

In the process of developing a React app with TypeScript, I encountered a challenging task involving formatting a complex nested JSON object into a specific structure. const data = { "aaa": { "aa": { "xxx&qu ...

Can a mapped union type be created in TypeScript?

Can the features of "mapped types" and "union types" be combined to generate an expression that accepts the specified interface as input: interface AwaActionTypes { CLICKLEFT: 'CL'; CLICKRIGHT: 'CR'; SCROLL: 'S'; ZOOM ...

There was a mistake: _v.context.$implicit.toggle cannot be used as a function

Exploring a basic recursive Treeview feature in angular4 with the code provided below. However, encountering an error when trying to expand the child view using toggle(). Encountering this exception error: ERROR TypeError: _v.context.$implicit.toggle i ...

What is the process for overriding the module declaration for `*.svg` in Next.js?

The recent modification in Next.js (v11.0.x) has introduced new type definitions: For next-env.d.ts (regenerated at every build and not modifiable): /// <reference types="next" /> /// <reference types="next/types/global" /> ...

What could be causing TypeScript to forego type inference and default to 'any' in this scenario?

While refactoring parts of our React app in TypeScript, I encountered a challenge that required me to use what I consider to be a less than ideal double type argument. I'm unsure if this is a bug in TypeScript or if there is some type ambiguity causin ...

What's the best way to use the keyboard's enter key to mark my to-do list

I'm looking to update my todo list functionality so that pressing enter adds a new todo item, instead of having to click the button. <h1 style="text-align:center">Todo List</h1> <div class="container"> ...

Exploring Vue.js lifecycle events and when to begin loading store properties (Vue.observable)

Currently, I am utilizing Vue.observable() for state management and it is crucial for two store properties to be fully loaded before most views are rendered by vue-router. I have attempted implementing the loading logic in various lifecycle events such as ...

Next.js Issue: Invariant error - page not correctly generated

I encountered a recurring error while attempting to build my project. Strangely, everything runs smoothly during development, but as soon as the build process is initiated, the following error presents itself: next build ▲ Next.js 14.1.0 - Environm ...

Deployment failure due to undetected development keys in gitignore

I have a TypeScript-coded Express server with three files for keys in the compiled and pre-compiled code: /// dev.ts - development keys const keys = { googleClientSecret: "GOOGLE_KEY", mongoURI: "mongodb+srv://MONGO_K ...

Generate a Jest dummy for testing an IncomingMessage object

I am facing a challenge in writing a unit test for a function that requires an IncomingMessage as one of its parameters. I understand that it is a stream, but I am struggling to create a basic test dummy because the stream causes my tests to timeout. : T ...

Encountering a sign-in issue with credentials in next-auth. The credential authorization process is resulting in a

I am currently facing an issue with deploying my Next.js project on Vercel. While the login functionality works perfectly in a development environment, I encounter difficulties when trying to sign in with credentials in Production, receiving a 401 error st ...

What is the process for clearing cache in inversifyJS?

In my simple application, I am using express server along with TypeScript. Initially, I can successfully reach my endpoint, but after updating the model, the old data persists. I have attempted the suggested approach mentioned here: How to disable webpage ...

Encountering an obscure package error while attempting to install an NPM package

After running the following command on my node application: npm install --save-dev typescript I encountered this error message: > <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="563a3f3426271667786e786e">[email pro ...

Why does isDisplayed method in Protractor return "No element found using locator" instead of a boolean value?

In my code, I've created a function called isElementDisplayed which features a call to element.isDisplayed. I'm curious as to why the isDisplayed method sometimes returns No element found instead of a boolean value. isElementDisplayed(element: ...

Ways to broaden React categories for embracing html attributes as props?

When designing a component that accepts both custom props and HTML attribute props, what is the best approach for creating the interface? The ideal interface should also accommodate React-specific HTML props, such as using className instead of class. Here ...

Creating interfaces within props is essential for defining the structure of components

I'm trying to pass an Interface to one of my components, but I'm running into some issues with my approach. Here's what I have so far: import { InterfaceType } from "typescript"; type Props = { dataType: InterfaceType } export default ...

What is the method for implementing two-way binding on a checkbox in Angular?

Within my accordion, I have a series of options in the form of checkboxes. Users are able to select these checkboxes, but I am seeking a way to pre-select certain checkboxes based on specific conditions. The challenge arises when these conditions are deter ...