Efficient solution for handling TypeScript interface properties based on previous property in a variety of scenarios

Background

Implementing this concept can be achieved in various ways. Here are some resources that discuss different approaches:

TypeScript: an interface property dependent on another

How to make Typescript interface property to be depended on another property

Typescript Interface - make type of one property dependent of type of another property


Approach

Through experimenting with different methods, I have found that the solution provided in the third link above is most suitable for my specific situation.

After testing various approaches, I realized that I had a particular question regarding this topic which warranted its own discussion.

Here is a snippet showcasing what I am working on:

enum MyEnum
{
    One = 'One',
    Two = 'Two',
    Three = 'Three',
    // And potentially more enums in the future.
}

interface MyInterface<T extends MyEnum>
{
    prop1: string,
    prop2: boolean,
    prop3: T,
    prop4: T extends MyEnum.One
           ? OneSpecificInterface
           : T extends MyEnum.Two
           ? TwoSpecificInterface
           : T extends MyEnum.Three
           ? ThreeSpecificInterface
           : // And so forth.
}

prop3 can take any value from MyEnum, and the type of prop4 depends on the value of prop3.

For instance, if prop3 is MyEnum.One, then the corresponding value passed to prop4 should be OneSpecificInterface (an interface containing specific values for that data type).

The unique values within each prop4 may vary significantly, making it impractical to create a single encompassing interface. However, there needs to be a unified interface covering these four properties, leading me to this methodology.


Question

While the current solution works effectively, it's evident that as MyEnum expands, the ternary operators within prop4 will become increasingly lengthy.

Is there a more concise or elegant approach to handle this logic within prop4?


If a more streamlined method using the existing framework is not available, it's not a major setback since the current system is straightforward enough - however, I am still curious and haven't managed to find a conclusive answer independently yet.

I am open to exploring alternative types of type-checking that achieve the same outcome, but after attempting several options, I have determined that this method offers the best balance between readability and effectiveness.

Answer №1

If you're looking to map enum members to interface types in TypeScript, you can take advantage of the keylike nature of enums which can have number or string values. This allows for a direct mapping between keylike types and arbitrary types using object types. For instance:

interface Definitions {
    [MyEnum.First]: FirstType,
    [MyEnum.Second]: SecondType,
    [MyEnum.Third]: ThirdType
    // etc
}

Instead of resorting to lengthy conditional types to find the value corresponding to an enum member, you can use indexed access types to retrieve it directly:

interface MyDefinition<T extends MyEnum> {
    property1: string,
    property2: boolean,
    property3: T,
    property4: Definitions[T]
}

This approach should scale well and deliver good performance. While this usage might be unconventional for interfaces (as there may not always be a readily available value of type Definitions), situations often arise where values of that type or a related one come into play naturally:

declare const definitions: Definitions;

function generateMockDefinition<T extends MyEnum>(t: T): MyDefinition<T> {
    return {
        property1: "abc",
        property2: true,
        property3: t,
        property4: definitions[t]
    }
}

The function above creates a mock MyDefinition<T> based on a provided value t of type

T</code, performing the lookup for <code>t
in an object of type Definitions. While this might not align with every specific use case, it demonstrates how this representation strategy can extend beyond simple type lookups.

Link to Playground with Code

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 issue with npm run build may be caused by a compatibility issue between TypeScript and lodash

Currently using typescript version 4.5.2 and lodash version 4.17.21 Running the command npm run build will trigger tsc && react-scripts build The following errors were encountered during the build process: node_modules/@types/lodash/common/objec ...

Retrieving information using React Query in TypeScript

When working with React Query and TypeScript, I encountered an issue with the getRecommendations hook. I wanted to only send the latest recommendations (I may create another hook for watchlist items in the future). The error I received was related to the q ...

The Angular filter feature operates on individual columns instead of filtering all columns simultaneously

Introduction I am currently working on implementing a feature in my Angular application where the column filter continuously updates the results based on the selected filters. The issue I'm facing is that when I select a filter in one column, it corr ...

The geolocation feature is operational in the browser test, but it is not functioning properly on the

I am currently creating an application that requires accessing the user's location at a specific point in time. To achieve this, I have utilized the ionic native geolocation feature which communicates with the Google API for reverse geocoding. Everyt ...

The compiler mistakenly infers an incorrect type while utilizing a default value for a discriminated union type

I am currently working on a simplified component: export type BaseProps = { size?: 'xs' | 'sm' | 'md' | 'lg'; } type ButtonAsButtonProps = Omit<React.ComponentPropsWithoutRef<'button'>, ' ...

Exploring the versatility of type extensions in TypeScript

Currently, there is an existing type named GeometryFeature: type GeometryFeature = { type: "Feature"; properties: { id: string; refId: string | null; rootZoneId: string; name: string; description: s ...

Typescript: The art of selectively exporting specific types

As I develop a Typescript component library, the API consists of two named exports: the component itself and a helper function to create an object for passing as a prop. The process is straightforward. For this library, I utilize an index.ts file as the m ...

The implementation of user context failed to meet expectations in terms of writing

I need some clarification regarding userContext in react with typescript. Initially, I define it in RubroContext.tsx import { createContext, useContext } from "react"; import { RubroType1, RubroType2 } from "../Interfaces/interfaces"; ...

What causes Next.js to struggle with recognizing TypeScript code in .tsx and .ts files?

Webpages lacking a declared interface load correctly https://i.stack.imgur.com/DJZhy.png https://i.stack.imgur.com/r1XhE.png https://i.stack.imgur.com/zXLqz.png https://i.stack.imgur.com/Z1P3o.png ...

Why does node.js struggle to find other TypeScript React components?

Currently, I am in the process of converting some react server-side rendering files to typescript. However, I have encountered an issue with locating my custom components. The file path structure is as follows: node_modules/ src/ page/Homepage.tsx ...

The element is absent in Type {}, however, it is mandatory in Type '&' and '&' cannot be designated to String Index Type Errors

I have a goal of passing the logged-in user's email address to a 'dict' as a key, fetching its corresponding token value, and using it as a query parameter for an API call. The user's email is retrieved from the user data upon login, sp ...

Guide to Making a Basic TypeScript Metadata Tag in Your Code

I'm looking for a way to format certain fields before sending them to the server-side. My goal is to serialize specific fields of my TypeScript classes using custom serializers. An example of what I'm aiming for is as follows: export class Pers ...

Is there a way to determine the number of code lines in all TypeScript files by utilizing tslint?

Looking to retrieve line count of code in all .ts files using tslint? Is there a way to obtain the total line count of code in all .ts files with the help of tslint? I haven't been able to find any information about this feature in the ...

What kind of conditions does SonarQube report as being uncovered?

According to SonarQube, there are uncovered conditions on all the arguments passed to constructors for each component in my Angular project, as well as any elements decorated with @Input(). What specific conditions is SonarQube referring to, and how can I ...

Using Higher Order Components (HOC) in combination with Redux compose and Typescript

I am trying to leverage two Higher Order Components (HOC) with Redux compose, but the compiler is not generating the correct types. The Compose function is defined in the Redux source code here source code. To better understand how the code works, you ca ...

Implementing a string replacement within an array of objects using TypeScript

I have a collection of array objects displayed below [ { "subjectID": 1 "Chosen" : "{subjectsChosen:Python,java,Angular}" "password": "{studentpw:123456abcd}" }, { "subjectID": 2 ...

Can inheritance pass constructor values in TypeScript/JavaScript?

I stumbled upon some code that seems confusing to me: I am still relatively new to TypeScript, but this doesn't seem like it should be functioning correctly: There are 2 classes involved in this scenario (pertaining to an automation framework, specif ...

I'm struggling to find the right Typescript syntax for defining a thunk function that returns a value while using React Redux Toolkit

Currently, I am utilizing TypeScript within a React Redux Toolkit project. While attempting to create an Async Thunk action function that is expected to return a boolean value, I found myself struggling with determining the correct TypeScript syntax: expor ...

Encountering an issue in Angular where data.slice is not functioning properly, resorting to using parseInt to convert strings into Date

Looking to convert the data retrieved from the database into numbers and dates with ease. One set of data is in milliseconds while the other is in timestamps. https://i.stack.imgur.com/HdM0D.png The goal is to transform both types into numbers first, the ...

Removing a row will always result in the deletion of the final row, as the index is returned as

In my Angular application, I have a Reactive Form with a feature that allows users to add or remove rows. Each row has its own delete button. However, there is an issue where clicking on the delete button for a specific item results in the last row being r ...