Create an entity with a field that holds a value type based on the value of another key field

Essentially, I am looking to create a customized "Pair" data type

For example:

type Pair<T extends Record<string, string | number>, K extends keyof T> = {
    field: K,
    value: T[K]
}

So, if we have:

type Rabbit = {
    name: string,
    age: number,
    weight: number
}

I would expect the following:

let namePair: Pair<Rabbit, keyof Rabbit> = {
    field: 'name',
    value: 123 //this should fail
}

Answer №1

You have the option to define a pair as a mapped type that assigns each key to a corresponding pair object:

type Pair<T extends Record<string, string | number>> =
  {[K in keyof T]: {field: K, value:T[K]}}[keyof T]

When applied to Rabbit, it generates a union of pairs:

type Test = Pair<Rabbit>
// {field: "name", value: string} | {field: "age", value: number} | {field: "weight", value: number}

This approach ensures the desired outcome with single pair declarations:

const namePair: Pair<Rabbit> = { field: 'name', value: "Bob" } // works fine

const weightPair: Pair<Rabbit> = { field: 'weight', value: 250 } // valid

const badPair: Pair<Rabbit> = { field: 'name', value: 606} // will not work

Moreover, this method is applicable to arrays as well:

const arr: Pair<Rabbit>[] = []

arr.push({ field: "name", value: "Bob" }) // acceptable

arr.push({ field: "weight", value: 250 }) // satisfactory

arr.push({ field: "name", value: 606 }) // fails to meet the criteria

TypeScript playground

Answer №2

You have the option to utilize a placeholder type T that serves as a key within the context of the Rabbit object.

type RabbitPair<T extends keyof Rabbit> = {
    rabbitField: T
    rabbitValue: Rabbit[T]
}

let field1 : RabbitPair<"name"> = { rabbitField: 'name', rabbitValue: 'Johnny'}
let field2 : RabbitPair<"age"> = { rabbitField: 'age', rabbitValue: 14}
let field3 : RabbitPair<"weight"> = { rabbitField: 'weight', rabbitValue: 'This is not a number'}

function test<T extends keyof Rabbit>(t: RabbitPair<T>){}

test({
  rabbitField: "age",
  rabbitValue: 23 // fails if there is string here
})

This approach may not be completely suitable for arrays:

const arr: RabbitPair<keyof Rabbit>[] = []

arr.push({
  rabbitField: "name",
  rabbitValue: "value" // numbers work here too, but nothing else
})

function push_rabbit<T extends keyof Rabbit>(t: RabbitPair<T>){
  arr.push(t)
}

push_rabbit({
  rabbitField: "name",
  rabbitValue: "value" // numbers don't work here
})

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

Angular's counterpart to IWebProxy

When using C#, I am able to: public static IWebProxy GetWebProxy() { var proxyUrl = Environment.GetEnvironmentVariable("HTTPS_PROXY"); if (!string.IsNullOrEmpty(proxyUrl)) { var proxy = new WebProxy { Address = new Ur ...

Identifying an Incorrect Function Call in a TypeScript Function from a JavaScript File [TypeScript, Vue.js, JavaScript]

I have a vue2 application and I am looking to incorporate TypeScript into some service files without modifying the existing js/vue files. To enable TypeScript support, I utilized vue-cli which allowed me to successfully add a myService.ts file containing ...

Currently, I'm harnessing the power of TypeScript and React to identify and capture a click event on a dynamically generated element within my document

Is there a way to detect a click on the <p> tag with the ID of "rightDisplayBtn"? I've tried using an onclick function and event listener, but neither seem to be working as expected. function addDetails() { hideModal(); addBook ...

The function 'appendChild' is not recognized on the type 'unknown'.ts(2339)

I'm encountering an issue while trying to integrate the Utterances component into my articles. Upon attempting to build the site, I receive the following error message: "Property 'appendChild' does not exist on type 'unknown' ...

Suggestions for importing by Typescript/VS Code

Imagine you have a file called a.ts that contains 4 named imports: export const one = 1 export const two = 2 export const three = 3 export const four = 4 Next, you have a file named b.ts and you want to import some variables from a.ts. import {} from &a ...

Error Message: ES5 mandates the use of 'new' with Constructor Map

Below is the code snippet: export class ExtendedMap<T, U> extends Map { constructor() { super(); } toggle(key: T, value: U) { if (this.has(key)) { super.delete(key); ...

Incorporate the ID of a newly created document into another document using Mongoose

Is there a way in mongoose to save the id of one document after creation into another document within the same collection using just a single query? ...

Customizing page layout for pages wrapped with higher-order components in TypeScript

Looking to add a layout to my page similar to the one in this link: layouts#per-page-layouts The difference is that my page is wrapped with a HOC, so I tried applying getLayout to the higher order component itself like this: PageWithAuth.getLayout Howev ...

Implementing validation logic on button click using TypeScript

I'm struggling to get my validation to work for empty fields using the method below. Can anyone provide some guidance or suggestions? Thanks. Here is my template: <form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="nobottommargin ad ...

Dealing with React and Firebase Authentication Errors: How to Handle Errors for Existing Accounts with Different Credentials

According to the documentation at https://firebase.google.com/docs/auth/web/google-signin#expandable-1, when error.code === 'auth/account-exists-with-different-credential', signInWithPopup() should return an error.email. However, in my specific c ...

The Angular EventEmitter does not broadcast any modifications made to an array

Below is the code snippet: notes.service.ts private notes: Array<Note> = []; notesChanged = new EventEmitter<Note[]>(); getNotes() { this.getData(); console.log('getNotes()', this.notes); ...

Populate input fields in HTML using Angular 4

Within my angular 4 project, I am facing the challenge of setting a value in an input field and in a MatSelect without relying on any binding. Here is the HTML structure: <div class="row search-component"> <div class="col-md-5 no-padding-rig ...

Tips for preventing repetition in http subscribe blocks across various components

Imagine a scenario where there is a service used for making HTTP request calls. There are two different components (which could be more than two) that need to send the same request using the same observables via this service. After receiving the result, it ...

Deploying Firebase functions results in an error

Just recently started exploring Firebase functions. Managed to install it on my computer, but running into an error when trying to execute: === Deploying to 'app'... i deploying firestore, functions Running command: npm --prefix "$RESOURCE_ ...

Deliver transcluded data to the descendant element of a hierarchical roster

I understand that there have been similar questions asked before, but my situation is slightly different. I am currently constructing a nested list and I want to include custom HTML content in each grandchild element alongside some common HTML. The problem ...

Upgrading from Angular 2 to 4 causes compilation failure in project

Recently, I upgraded my Angular project from version 2 to version 4. The steps I followed for this upgrade are as follows: 1- Deleted the /node_modules/ folder 2- Executed the following command: npm install @angular/common@latest @angular/compiler@lat ...

Webpack is throwing an error due to the Vue component type being labeled as "any"

When using ts 4.2.4 and Vue3, I encountered a strange error while building my vue project: > <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c3a2a7aeaaededb3a2a0a083f3edf2edf3">[email protected]</a> build > v ...

What is causing the TypeScript error in the MUI Autocomplete example?

I am attempting to implement a MUI Autocomplete component (v5.11) using the example shown in this link: import * as React from 'react'; import TextField from '@mui/material/TextField'; import Autocomplete from '@mui/material/Autoco ...

Combine all the missing observables in RxJS into a single array

In my code, I am creating a NavBar with items that may require fetching extra information from an API and adding it to the subtitle field. I want to transform this into an Observable<NavItem[]> so that it can be rendered using an Async Pipe. Curren ...

Can anyone assist me with creating a custom sorting pipe in Angular 2?

*ngFor="let match of virtual | groupby : 'gameid' I have this code snippet that uses a pipe to group by the 'gameid' field, which consists of numbers like 23342341. Now, I need help sorting this array in ascending order based on the g ...