Ensuring the accurate usage of key-value pairs in a returned object through type-checking

After generating a type definition for possible response bodies, I am looking to create a function that returns objects shaped as { code, body }, which are validated against the typing provided.

My current solution looks like this:

type Codes<Bodies> = keyof Bodies

type ResponseCodeBodyPair<Bodies extends Record<string, unknown>> = {
  code: Codes<Bodies>,
  body: Bodies[Codes<Bodies>]
}

function endpoint(): ResponseCodeBodyPair<ResponseMap> {
  if (Math.random()) {
    return { code: '200', body: { 'ok': true } }
  }
  if (Math.random()) {
    return { code: '404', body: { 'error': true, type: 'NotFound' } }
  }
  if (Math.random()) {
    return { code: '500', body: null }
  }

  // This should fail since { ok: true } is not a valid body for 404.
  return { code: '404', body: { 'ok': true } }
}

This implementation ensures that there are no unknown status codes returned by the function and that all returned bodies match one of the predefined shapes.

However, it does not validate the correctness of code-to-body pairing, as demonstrated by the last return statement.

I am searching for a way to restrict the type to the exact body shape for each status code. Something like:

type ResponseCodeBodyPair<Bodies extends Record<string, unknown>> =
  Code extends keyof Bodies ?  { code: Code, body: Bodies[Code] } : never

Unfortunately, my attempts have been unsuccessful so far. Is achieving this level of restriction even possible?


P.S. I acknowledge that having a type

{ code: '200', body: … } | { code: '404', … } | …
would simplify things, but that is not an option at the moment. Therefore, I need to find a way to transform the map/record into this union type.

Answer №1

I have finally figured out the correct approach to tackle this problem.

By transforming the { [Code]: Response } object into a

{ [Code]: { code: Code, body: Response } }
 object and accessing its values using T[keyof T], I am able to obtain the precise type required for my task.

type ResponseCodeBodyPairMap<M> = {
  [Code in keyof M]: { code: Code, body: M[Code] }
}

type Values<T> = T[keyof T]

type ResponseCodeBodyPair<M> = Values<ResponseCodeBodyPairMap<M>>

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

Is there a way in Angular to activate the contenteditable feature through a controller?

I have a collection of items, and the currently selected one is displayed in more detail on another section of the screen. The detailed section allows users to modify specific parts of the chosen item using contenteditable. When a user adds a new item to ...

What are some ways to postpone the execution of a function in React or NextJs?

How do I automatically redirect to the home page after 5 seconds of accessing a page in NextJS? I attempted to achieve this using the following code: useEffect(() => { const timer = setTimeout(() => { redirect('/home') ...

Accelerate the generation speed by using JsPDF to convert canvas to PDF

I need to optimize the download speed of a specific div on my webpage as a PDF using jspdf and canvas. Currently, it takes 2 or 3 seconds which is too slow for my client's liking. Is there a way to make this process faster? Additionally, the file size ...

Is there a way to verify the presence of months in a column related to departments?

Is there a way to validate whether the current row aligns with the current column month and year? If not, can it be set to 0. Let's consider the current dataset. Presenting my resultData https://pastebin.com/GHY2azzF I want to verify if this data ...

What is the best way to retrieve information from an array containing objects in AngularJS?

Check out this json data: { topalbums: { album: [ { name: "The Very Best of Cher", playcount: 1634402, mbid: "a7e2dad7-e733-4bee-9db1-b31e3183eaf5", url: "http://www.last.fm/music/Cher/The+Very+Bes ...

Leverage information extracted from the Node.js function

As I dive into the world of NodeJS, a particular issue arose while working with the getCurrentWeather() function. It's asynchronous nature means that it loads instantly upon app start and writes data to variables. However, when attempting to use these ...

Differences in outcomes have been observed between the elementLocated and findElements functions

Currently, I am in the process of developing Webdriver automation for a specific web application. As part of this process, I have created a test that seems to be functioning well most of the time but occasionally encounters an issue. it('verifies pre ...

How important is a neglected parameter in the world of JavaScript?

Curious about the value of an ignored parameter in JS? Imagine a function that requires 2 values as parameters, but you only provide one during the call. What happens to the other parameter? You might think it's undefined, but the code below only show ...

Scrolling text box utilizing Jquery

Currently, I am utilizing a scrolling box that functions well * view here * under normal circumstances. However, when there is an extensive amount of content below it, such as: <article class="content"> ...

Tips for contacting the giveaway host after the giveaway has finished

I am currently utilizing the Discord Giveaways module. I am interested in learning how to send a direct message to the host of the giveaway once it has ended. I haven't quite figured out how to do that yet. Here is what I have gathered so far: manag ...

What are the best practices for creating a contemporary website that functions effectively on IE7?

After creating several websites for my clients, I discovered that they are not functioning well in IE7. Unfortunately, there is still a small percentage of people using IE7. Can anyone suggest a super quick solution to fix this issue? Feel free to recomm ...

Creating assets from typescript plugins in Angular 6: A comprehensive guide

Situation I am currently in the process of migrating from Angular 4 and Angular Seed to Angular 6 and Angular CLI. Challenge One issue I am facing is with dynamic loading of plugins within a component using SystemJS. SystemJS.import("client/plugins/" + ...

Unlocking the capabilities of Chrome extensions using Angular 2 and TypeScript

Currently attempting to develop a chrome extension using angular2 and typescript, I have hit a roadblock in trying to access the chrome API (in this case, chrome.bookmarks). I have successfully gained access to the chrome object by following guidance from ...

Insert this HTML table along with radio buttons into an Excel spreadsheet

My HTML table contains rows with a variety of content, including plain text characters and elements like radio buttons and lists. I want users to be able to copy-paste the table into MS Excel as plain text and have the radio button values replaced with "c ...

Access-Control-Allow-Origin does not permit AngularJS Origin http://localhost:8080

I'm working on a web application using AngularJS. It's an admin interface that depends on a json-rpc API hosted on a different domain. When testing in my local environment, I encountered the error "Origin http://localhost:8080 is not allowed by ...

Destructuring and For of Loop in ES6: Capturing the first object only

Working with my react app, I have a specific object called 'topics' that I am looping through: const topics = [ {name: 'Mon', color: 'blue', id: 1}, {name: 'Tue', color: 'red', id: 2}, {name: 'W ...

What is the best way to manage multiple directives from a parent component?

Within my app-landing component, I have implemented multiple typeOut directives. These directives are responsible for gradually writing out text within their respective elements. While this functionality is working as intended, I now seek to exert control ...

Embarking on a New Project with Cutting-Edge Technologies: Angular, Node.js/Express, Webpack, and Types

Recently, I've been following tutorials by Maximilian on Udemy for guidance. However, I have encountered a roadblock while trying to set up a new project from scratch involving a Node/Express and Angular 4 application. The issue seems to stem from the ...

What distinguishes between the methods of detecting falsy and truthy values?

While working with JavaScript / Typescript, I often find myself needing to verify if a length exists or if a value is true or false. So, the main query arises: are there any differences in performance or behavior when checking like this... const data = [ ...

Execute a jQuery focus() function on a textarea element within a dynamically added form on the webpage

Whenever I dynamically insert a new form onto the page using clone(), my goal is to have the textarea within the form automatically focused. However, despite trying out the code below, it seems like it's not functioning as expected: .on('click&a ...