Determining object types based on sibling properties in Typescript

Can you define properties of an object based on another property within the same object?

How do I determine the types for values based on the value of the type property?

type StepKeys = "Test1" | "Test2";

interface objectMap {
    "Test1": {
        name: string
    },
    "Test2": {
        age: number
    }
};

interface Step<T extends StepKeys = StepKeys> {
    type: T;
    value: objectMap[T]
};

const steps: Record<string, Step> = {
    "Step1": {
        type: "Test1",
        value: {

        }
    }
}

The possible types for values are a union of

{ name: string; } | { age: number; }
.

Is it feasible to deduce its potential values?

Typescript

Answer №1

If you want to effectively distinguish this combination, it is important to adjust the type parameter and avoid using a union. Instead, aim to elevate the union by one type. This way, each Step type will have its own distinct type parameter, rather than a combination of possible type parameters. It may seem like a small change, but it achieves the desired outcome.

const steps: Record<string, Step<'Test1'> | Step<'Test2'>> = {
    "Step1": {
        type: "Test1",
        value: {
            // infer ?
            age: 11,
// ^^^ correctly throws error 
// Object literal may only specify known properties, and 'age' does not exist in type '{ name: string; }'.(2322)
            name: 'test',
        }
    }
}

If there are numerous key/value pairs, an indexed access map type can automatically create a discriminated union.

type Steps = Record<string, {
    [key in StepKeys]: Step<key>
}[StepKeys]>

Check it out on TS Playground

Answer №2

If you're looking to accomplish your goal, there is a somewhat unconventional method that can get the job done. The downside is that it requires manual input for each key, which hinders its ability to be easily customizable or scalable.

interface PeopleData {
    withName: {
        name: string
    },

    withAge: {
        age: number
    }
};

type Person =
    { type: 'withName', value: PeopleData['withName'] } |
    { type: 'withAge',  value: PeopleData['withAge']  }

const personInfo: Record<string, Person> = {
    john: {
        type: 'withAge',
        value: {
            age: 10  
        }
    }
}

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

Confirming the existence of a folder with AngularJS

Currently, I am attempting to determine if a folder exists so that I can make decisions on which files to include using ng-include. This is what I have so far: $scope.isVisible = { buttons: checkForClientOverwride('buttons'), it ...

Exploring JavaScript and the Power of Manipulating the DOM (Adding and Removing Elements)

Currently, I am attempting to dynamically generate li elements inside an array and include a 'delete' button within each li. The goal is that clicking on the delete button will remove that specific li from the array. Although this may appear str ...

Tips for saving images in an S3 bucket

Within my express application, I currently save images to a directory in my repository. However, I am beginning to think that this approach may not be ideal and I am considering using AWS S3 as an alternative storage solution. Since I have never worked w ...

Searching for 10 random data records within a collection of 100 in MongoDB

Can MongoDB retrieve a specific number of random documents in one query? For example, I currently load all the documents in the collection on the JavaScript side, which is inefficient. I'm wondering if there's a better way to achieve this with a ...

Is it possible to load Angular.js without any references?

In the process of reverse engineering a User Interface that is operational but has a few bugs. Created using HTML, CSS, and JavaScript with data acquired through a REST API. The interface is designed for a Windows environment. While examining the index.ht ...

Ways to create interactive multiple dropdown menu using vue-multiselect

I'm not sure if it's possible to achieve what I want with Vue for a specific component by changing its data and automatically loading it. Below is my expectation (tried in jQuery) var data = {country:{type:'dropdown',values:[' ...

When you hover over the button, it seamlessly transitions to a

Previously, my button component was styled like this and it functioned properly: <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', height: 48, padding: '0 ...

What steps are involved in generating JSONP from an external JSON source?

I have a dilemma with two different domains: www.domain1.com and www.domain2.com Domain1 hosts a simple JSON feed. My goal is to retrieve the JSON feed from domain1 and display it in a module on domain2. After some research, I've come across sugges ...

Automated library that refreshes the webpage instantly upon any server modifications

Seeking a Javascript solution to automatically refresh a webpage when the server version is updated. Update: I am aware of the technical aspects involved and how to implement this feature. However, I am interested in finding an existing solution that I ca ...

The issue of elements not appearing seems to be related to the passing of parameters to the state

In my AngularJS application, I have a form where users must input an application number. Upon successful entry, the corresponding data should be displayed below. This data can only be accessed through the scope if passed as a parameter to the current state ...

Tips for receiving accurate HTML content in an Ajax request

I have used an Ajax call to fetch data from a function that returns an entire HTML table. $.ajax({ url: "/admin/project/getProjectTrackedTimes", headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('cont ...

What are the recommended steps for making an AJAX request to execute C code smoothly?

I am currently working on designing a web interface for a device that is powered by an underlying C application and involves intensive hardware operations. The primary function of the web interface is to display status information to the user. Specifically ...

Work with JSON array objects

I am a novice in javascript and JSON. I have a requirement to process each JSON object, as shown in the example prototype below. Can someone please assist me in solving this problem? I need to log each room number given in the JSON sample below. How can I ...

Troubleshooting the EACCESS error in Node.js on OpenShift

I'm having trouble getting the application to start properly, as I keep receiving an EACCESS error: Error: listen EACCES Below is the code snippet causing the issue: var server_port = process.env.OPENSHIFT_NODEJS_PORT || 8080 var server_ip_address ...

How can I access app.locals.<myvar> within routes/index.js in Express.js 4?

I am trying to access my own variable, `app.locals.port`, from the app.js file within my routes/index.js file. app.js: app.locals.port = 3001; var index = require('./routes/index'); app.use('*', index); // use router in ./routers/inde ...

AngularJS controllers and global scope management

As per AngularJS's tutorial, the controller function is said to reside within the global scope. http://docs.angularjs.org/tutorial/step_04 Are the controller functions themselves automatically encapsulated in a scope, or do they actually exist withi ...

Unusual grey rectangle (similar to a selection tool) found in the top left corner of a contenteditable div on

I recently utilized Vue to create a contenteditable div on my website. However, I encountered an issue where a gray area appeared on the upper left corner when hovering over the div. Oddly enough, this problem only occurred in Chrome, while Safari and Fire ...

Leverage the power of function overloading in TypeScript for efficient code

How can function overloading be reused effectively in TypeScript? Consider a scenario where a function is overloaded: function apply(value: number): number; function apply(value: string): string; function apply(value: any): any { return value; } No ...

The situation where a Javascript switch case continues to fall through to the default case even when a 'break' statement

I am currently setting up a node.js discord bot that utilizes firebase to save a user's status. My switch statement is functioning smoothly, handling each command effectively. The default case looks like this: default: message.reply("Unknown comm ...

Searches for Mongoose Queries

Here is the table structure: "products": [ { "reviews": [], "_id": "5ece6d09cab302507c5d147e", "category": { "_id": "5ece6c07cab302507c5d1478", "type": "Marketing", = I need to r ...