Creating a TypeScript interface where the type of one property is based on the type of another property

One of my Interfaces has the following structure:

export enum SortValueType {
  String = 'string',
  Number = 'number',
  Date = 'date',
}


export interface SortConfig {
  key: string;
  direction: SortDirection;
  type: SortValueType;
  options?: {
    sortBy?: 'year' | 'day';
  };
}

I want to enhance this Interface by restricting the possible values of options.sortBy based on the value of type. I am designing functions that rely on the type property, so it should not be allowed to have a scenario where type is string and options.sortBy is set to year.

Below is the implementation of the getIteratee function:

 private getIteratee(config: SortConfig) {
    if (config.type === SortValueType.String) {
      return item => _lowerCase(item[config.key]);
    }

    if (config.type === SortValueType.Date) {
      const sortBy = _get(config, 'options.sortBy') as 'year' | 'day';

      if (!!sortBy && sortBy === 'year') {
        return item =>
          !!item[config.key]
            ? new Date(item[config.key]).getFullYear()
            : undefined;
      }

      if (!!sortBy && sortBy === 'day') {
        return item =>
          !!item[config.key]
            ? new Date(item[config.key].toJSON().split('T')[0])
            : undefined;
      }
    }

    return config.key;
  }

I am also open to exploring more versatile solutions.

Answer №1

Here is a possible solution for your needs:

export enum SortValueType {
  String = 'string',
  Number = 'number',
  Date = 'date',
}

type SortDirection = string;

export interface SortConfig<T extends SortValueType> {
  key: string;
  direction: SortDirection;
  type: T;
  options:
    T extends SortValueType.Date
      ? { sortBy: 'year' | 'day'; }
      : { sortBy?: undefined };
}
// OK:
let dateConfig: SortConfig<SortValueType.Date> = {
  key: "key",
  direction: "left",
  type: SortValueType.Date,
  options: { sortBy: 'year' }
}

// Not OK:
let numberConfig: SortConfig<SortValueType.Number> = {
  key: "key",
  direction: "left",
  type: SortValueType.Number,
  options: { sortBy: 'year' }
}

Playground link

The sortBy?: undefined is necessary to prevent the addition of extra properties as TypeScript does not restrict them by default. Using just {}, { sortBy: 'year' } would be considered valid, being assignable to {}.

You can adjust the options type accordingly based on your specific requirements for each type.

To optimize getIteratee functionality, consider changing the parameter to SortConfig<any>, establishing a type guard and utilizing it to refine the type in each scenario. For example:

function isConfigType<T extends SortValueType>(
    config: SortConfig<any>, type: T
): config is SortConfig<T> {
  return config.type === type;
}

function getIteratee(config: SortConfig<any>) {
    if (isConfigType(config, SortValueType.String)) {
        // The type of 'config' here is SortConfig<SortValueType.String>;
    } ...
}

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

Error message: The function URL.createObjectURL is not recognized in this context | Issue with Antd charts

Currently, I am working on integrating charts from antd into my TypeScript application. Everything runs smoothly on localhost, but as soon as I push it to GitHub, one of the tests fails: FAIL src/App.test.tsx ● Test suite failed to run TypeError: ...

Building Unique Password Validation with Angular 5

I'm attempting to implement custom password validation for a password field. The password must be a minimum of 8 characters and satisfy at least two of the following criteria but not necessarily all four: Contains numbers Contains lowercase letters ...

"Firebase function fails to return Typescript class variable, resulting in 'undefined'

Being someone with a background in python/golang, I am now delving into ionic2. There seems to be an issue that I can't quite figure out due to my current level of knowledge in this stack. Perhaps I just need a way to reference the outer scope of this ...

Tips for showcasing individual row information in a popup window with Angular 9

Here is the code snippet for utilizing the open dialog method in the component: import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { AuthService } from '../_services/auth.se ...

Challenge when providing particular strings in Typescript

Something seems to be wrong with the str variable on line number 15. I would have expected the Typescript compiler to understand that str will only ever have the values 'foo' or 'bar' import { useEffect } from 'react' type Ty ...

Experiencing a 404 ERROR while attempting to submit an API POST request for a Hubspot form within a Next.js application

Currently, I am in the process of developing a Hubspot email submission form using nextjs and typescript. However, I am encountering a couple of errors that I need help with. The first error pertains to my 'response' constant, which is declared b ...

Ways to return bsDateRangePicker to its default value

I'm currently working on creating reactive forms using Angular 9 and integrating ngx-bootstrap. One issue I am facing is with the daterangepicker functionality. Whenever I utilize the form.reset() function, it clears the input field entirely instead o ...

Executing callback in the incorrect context

I'm facing an issue and can't seem to navigate through it. I am setting up a callback from a third party directive, but the callback is not returning with the correct scope. This means that when it reaches my controller, this refers to some other ...

Learn how to effectively declare data as global within Angular2 or Typescript

I am facing an issue with fetching the id inside the Apiservice despite being able to get it in the console. Can anyone provide assistance on how to solve this problem? TS: deleteProduct(index,product) { var token = this.auth.getAccessTokenId(); ...

Did not adhere to regulations

Currently, I am in the process of developing a React app. However, when I attempt to initiate my project using npm start in the terminal, I encounter an error message on the browser. https://i.stack.imgur.com/wej1W.jpg Furthermore, I also receive an erro ...

Make sure to call the loader function in React Router only when there are path params present

I'm currently implementing the new React Router, utilizing loader functions to fetch data based on the loaded element. My goal is to have certain APIs called regardless of the route, with additional APIs triggered for specific routes. However, I&apos ...

Storing information using the DateRangePicker feature from rsuite - a step-by-step guide

Currently, I am working on storing a date range into an array using DateRangePicker from rsuite. Although my application is functioning correctly, I am encountering some TypeScript errors. How can I resolve this issue? import { DateRangePicker } from " ...

Issue with Angular: boolean value remains unchanged

Currently, I'm encountering an issue with my application. My objective is to establish a list containing checkboxes that toggle their values between true and false when clicked. Sounds simple enough, right? Below is the HTML code snippet: <l ...

Using Material UI Slider along with Typescript for handling onChange event with either a single number or an

Just diving into Typescript and encountered an issue with a Material UI Slider. I'm trying to update my age state variable, but running into a Typescript error due to the typing of age being number and onChange value being number | number[]. How can I ...

Provide a boolean value of true or false to indicate whether all delete operations were successfully completed

Currently, I am using Sequelize, GraphQL, and Typescript for my coding. Within my database, I have two tables named RecordInformation and OtherDescription. The RecordInformation table contains a foreign key called "OtherID" which references the OtherDescri ...

Troubleshooting an angular problem with setting up a dynamic slideshow

I am currently working on building a slideshow using plain HTML, CSS, and JavaScript. I referred to the following example for guidance: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_slideshow_auto However, despite implementing the code prov ...

leveraging services in Angular 4's router system

Below is the route setup: export const routes: Routes = [ {path: '', redirectTo: '/login', pathMatch: 'full'}, {path: 'login', component: LoginComponent, canActivate: [dbs.ConfigGuard]}, {path: '**& ...

Eliminate the hashtag (#) from the URL in Angular 11

I am facing an issue with removing the # from the URL. When I try to remove it, the application encounters a problem upon deployment to the server. Upon page refresh, a 404 error status is returned. For instance: https://example.com/user/1 (works) https ...

The argument of type 'NextRouter' cannot be assigned to the parameter of type 'Props' in this scenario

In my component, I am initializing a Formik form by calling a function and passing the next/router object. This is how it looks: export default function Reset() { const router = useRouter(); const formik = useFormik(RecoverForm(router)); return ( ...

Error TS2339: The member 'sort' is not found in the type 'BehaviorSubject<AbstractControl[]>'

Looking to create a dynamic table using Angular material where I can dynamically add or remove data. As a newcomer to Angular, I found the following StackBlitz helpful in setting up the table: https://stackblitz.com/edit/angular-material-editable-table-fa ...