Typescript interface with conditional fields based on the value of another field

I am currently working on building an interface that will include mandatory fields based on the value of another field.

For instance:

const schema = {
    str: { type: 'string' },
    nbr: { type: 'number' },
    bool: { type: 'boolean' },
    date: { type: 'date' },
    strs: { type: ['string'] },
    obj: { type: 'object' },
} as ISchema;

In the above code snippet, I want the system to notify me if the obj field is missing a property because its value for type is set to 'object'.

After some trial and error, I managed to achieve this using the following code structure:

interface SchemaOptionsObject {
    type: 'object' | ['object'] ;
    properties: ISchema;
}
interface SchemaOptionsString {
    type: 'string' | ['string'] ;
}
interface SchemaOptionsNumber {
    type: 'number' | ['number'] ;
}
interface SchemaOptionsBoolean {
    type: 'boolean' | ['boolean'];
}
interface SchemaOptionsDate {
    type: 'date' | ['date'] ;
}

type SchemaOptions = SchemaOptionsString | SchemaOptionsNumber | SchemaOptionsBoolean | SchemaOptionsDate | SchemaOptionsObject;

export interface ISchema {
    [key: string]: SchemaOptions;
}

However, I noticed that my solution involved a lot of repetition. In an attempt to optimize the code, I encountered a challenge:

export type SchemaAllowedTypes = 'string' | 'number' | 'boolean' | 'date' | 'object';

type SchemaOptionsObject<T extends SchemaAllowedTypes> =
    T extends 'object' ?
        { properties: ISchema } :
        {};

type SchemaOptions<T extends SchemaAllowedTypes> = {
    type: T | T[];
} & SchemaOptionsObject<T>;

export interface ISchema {
    [key: string]: SchemaOptions<SchemaAllowedTypes>;
}

It seems that the code snippet above does not work as expected due to the restriction imposed by T extends 'object'. Is there any specific keyword that can help me validate the value of T in this scenario?

Do you think I am approaching this problem in the wrong way? Your assistance would be greatly appreciated!

Thank you for your support!

Answer №1

What do you think of this solution?

export type AllowedDataTypes = 'string' | 'number' | 'boolean' | 'date' | 'object';

interface GenericSchemaOptions<T extends AllowedDataTypes>{
    type: T | [T] ;
}
interface ObjectSchemaOptions extends GenericSchemaOptions<"object">{
    properties: DataSchema;
}
type SchemaOptions = GenericSchemaOptions<"string"|"number"|"boolean"|"date"> | ObjectSchemaOptions

export interface DataSchema {
    [key: string]: SchemaOptions;
}

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

Utilizing Array Types in TypeScript Interfaces

After creating an interface for Velocityjs to use in TypeScript, I encountered a challenge with making interfaces for array types. Specifically, when working on a function for generating calls for the Velocity.RegisterEffect method from the Velocity UI Pac ...

Angular2: Adding a new array to all objects within an existing array

I am working with a REST API that delivers data in JSON format. I have stored this data in an array of objects, but now I want to add a new empty array to each object which is proving to be challenging. Below is a snippet of how my REST API data is structu ...

React Native bottom tab navigator not changing between tabs

Hi, I'm new to React Native and I think I might have a structural issue because I can't figure out what I'm doing wrong. I'm trying to create 4 tabs, but when I click on each tab, it doesn't take me to the next page. Nothing happe ...

Setting up the 'nativescript-stripe' plugin in your NativeScript Vue project for seamless integration

Trying to integrate the «nativescript-stripe» plugin into my Nativescript Vue app has been a challenge. The documentation and demos on the plugin's GitHub are geared towards Angular and TypeScript, making it difficult to adapt for Vue. Can anyone pr ...

Identifying one of the two possible return types automatically

In my code, there is a function named isDone() that will return data from either an array of hashes or a dictionary of hashes: public async isDone() { this.startDelayedTasks(); await Promise.all(this._tasks); const hadErrors = this._failed.length &g ...

Utilize ngx-select-dropdown to showcase various displayKey values at once

I am currently utilizing the ngx-select-dropdown plugin with a search feature, and I am looking to specify multiple values for the displayKey property such as: firstname and lastname. Here is the configuration object I am using: dropdownconfig = { di ...

Is it possible for the <i></i> tag to be a self-closing tag in JSX?

I recently adhered to the Airbnb React/JSX style guide which mentions the importance of "Always self-closing tags that have no children". Does this rule apply to the <i></i> tag as well, considering it typically has no children? Would it be ac ...

Leveraging the keyof keyword to access a specific property within a type

I have a variety of types that I need to work with. For example: type Type = { prop1: number; prop2: string; prop3: someOtherType } type Props = keyof Type I am aware that using an "indexed access type" allows me to extract the type of propN, ...

The quantity of documents queried does not align with the number of data counts retrieved from Firestore

Facing an issue with calculating the Firestore read count as it keeps increasing rapidly even with only around 15 user documents. The count surges by 100 with each page reload and sometimes increases on its own without any action from me. Could this be due ...

Tips for Effectively Declaring a Variable with React's useState

How should I correctly specify variable types in useState? In the code below, the value for alert must be either "success","warning", "error", or "info" const [alertValue, setAlertValue] = useState("error" ...

What is the best way to implement Infinite scroll alongside Virtual scroll in Ionic 3?

Having recently delved into the world of Ionic and Angular, I am encountering some difficulties with implementing Infinite scroll alongside Virtual scroll. Despite pushing data such as images, text, and click functions from TypeScript, only empty Ionic car ...

What is the best way to ensure that Interface (or type) Properties always begin with a particular character?

I attempted to tackle this assignment using template literals, but unfortunately, I wasn't successful. Here is the interface that I am working with: interface SomeInterface { '@prop1': string; '@prop2': string; '@ ...

Angular2: The '@Component' decorator does not contain a 'directives' property in its configuration object

After creating a directive to auto-expand a textbox, I encountered an error when implementing it into the component. myAppComps.ts https://i.sstatic.net/rZHQc.png NPM RUN BUILD https://i.sstatic.net/DDY4k.png auto-grow.directives.ts https://i.sstat ...

Following the npm update, encountering errors with webpack

Upgrading the npm package to version 8.2.0 has caused issues in my React application. Here is a screenshot of the problem: https://i.stack.imgur.com/noQIz.png These are the error messages I see in the console: [HMR] Waiting for update signal from WDS.. ...

It's conceivable that the item is 'null'

I am encountering Typescript errors in my code that are related to parameters I am receiving from a previous screen. This is similar to the example shown in the React Navigation documentation found at https://reactnavigation.org/docs/params/. interface Pix ...

Joining subscriptions: How to synchronize completion of multiple subscriptions in a single operation

As a newcomer to RXJS, I am facing a particular challenge that I need help with. I have two API calls, where the second call is dependent on the result of the first one. My issue lies in needing to handle both calls within a single subscription so that th ...

Modifying the appearance of a Component within a NavLink

I'm currently working on a navbar using NavLink from React-Router-Dom. It's fine to use the 'isActive' prop to style the active Link, but I'm stuck on how to style the subelements inside it. For more specific details, please take a ...

Troubleshooting: Resolving the error message 'Unable to assign to Partial<this>' within a subclass method

If I call the base class's update method using a subclass instance, it functions as expected. However, I encounter an error when attempting to do so within a subclass method: Argument of type '{ prop: number; }' is not compatible with par ...

TypeScript: implementing function overloading in an interface by extending another interface

I'm currently developing a Capacitor plugin and I'm in the process of defining possible event listeners for it. Previously, all the possible event listeners were included in one large interface within the same file: export interface Plugin { ...

Step-by-step guide on dynamically setting a value in a select option using Angular

Here is a select option that I have: <div class="input-field col width"> <select name="GENDER" class="genderSelect" ng-model="view.activeResource.ValueType" ngModel> <option value="" disabled selected>Choose your option</option& ...