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

Placing a MongoDB query results in an increase of roughly 120MB in the total JS heap size

I'm puzzled by the fact that the heap size increases when I include a MongoDB database query in a function within my controller. Here is the code for my router: import { Router } from "express"; import profileController from '../contro ...

Is it possible to evaluate a conditional in Angular after retrieving values from a subscription in an observable?

Objective: Verify conditional statement after retrieving data from an array Attempts Made: I explored various articles on SO with similar queries, but they didn't quite match my situation. I need to ensure that the entire Array is populated before ev ...

Having trouble with my React component timer not functioning properly

How can I utilize the Header Component as a Clock timer for my webpage to update every second? Despite searching on Google, I couldn't find examples that match my requirements. Why is the tick() function not functioning properly even though there are ...

What is the reason behind the lack of covariance in an interface when using a type of T[keyof T]?

I'm attempting to enable type covariance so that Type<Cat> can be treated as a Type<Animal>, for instance, treating a list of Cats as a list of Animals. However, using the type T[keyof T] in a method within the Type interface seems to hind ...

Utilizing TypeScript's Exclude feature with a generic type

An update to dependencies in my TypeScript-based Nodejs project has led to compilation errors when working with a generic class that manages mongoose models. The root cause appears to be related to how TypeScript handles generic types. To illustrate the i ...

Checking the types of arrays does not function properly within nested objects

let example: number[] = [1, 2, 3, 'a'] // this code block correctly fails due to an incorrect value type let example2 = { demo: 1, items: <number[]> ['a', 'b'], // this code block also correctly fails because of ...

How can we optimize component loading in react-virtualized using asynchronous methods?

In my component, I have implemented a react-virtualized masonry grid like this: const MasonrySubmissionRender = (media: InputProps) => { function cellRenderer({ index, key, parent, style }: MasonryCellProps) { //const size = (media.submiss ...

angular2 the custom template validator is encountering outdated values

I've been struggling with a specific issue for quite some time now. I'm working on setting up an Angular 2 custom validator that checks if a number falls within a certain range. When used as follows, everything functions correctly: <input typ ...

Enable Ace Editor's read-only mode

I'm having some difficulty getting my ace-editor to work in read-only mode. I'm working with Angular 9 and I've attempted the following approach: In my HTML file, I have the following code: <ace-editor mode="java" theme="m ...

Guide to Automatically Updating Angular Component When Service Data Changes

I am currently working on an Angular application that features a sidebar component displaying different menu items based on the user's data. The sidebar should only display an option for "Empresas" if the user has not created any company yet. Once a c ...

Is it possible to loop through each row in a table using Cypress and execute the same actions on every iteration?

I have a frontend built with html/typescript that features a table of variable length containing action buttons in one of the columns. I am looking to create a Cypress test that will click on the first button of the first row, carry out a specific task, an ...

Slim API receives a request from Ionic 5

Seeking assistance with making a GET request from my Ionic application to an API constructed using the Slim Framework. Below is the code snippet of the API: <?php header('Access-Control-Allow-Origin: *'); header('Content-Type: applicati ...

What could be causing TypeScript to struggle with verifying the return type of a function?

I am facing an issue with a function that is supposed to return NetworkState. However, despite the code clearly showing that the function does not return the correct type in most cases, TypeScript does not flag any errors. Can someone point out what I migh ...

Bidirectional data binding in angular 12 reactive forms

After working with angular for a while, I encountered an issue while trying to implement two-way binding. The code snippet below is where I'm facing difficulty. Since the use of [(ngModel)] has been deprecated in Angular 12 within formGroup, finding ...

Deactivating Bootstrap Modal in Angular

Looking for advice on managing a Bootstrap Modal in Angular 7 I have a Form inside a Bootstrap Modal that I need to reset when the modal is closed (by clicking outside of it). Despite searching on Google, I haven't been able to find a solution. Any ...

Tips for showcasing the information from a JSON document in React

I have a JSON file stored statically in my public directory and I'd like to show its content within a React component (specifically NextJS). The goal is to simply render the JSON as it is on the page. import data from '../../public/static/somedat ...

Creating a conditional statement within an array.map loop in Next.js

User Interface after Processing After retrieving this dataset const array = [1,2,3,4,5,6,7,8] I need to determine if the index of the array is a multiple of 5. If the loop is on index 0, 5, 10 and so on, it should display this HTML <div class="s ...

Issue: The TypeError reported is due to the absence of any definition for "_co.category.category_type"

I have two models: CategoryTypes and Categories. Each category type contains multiple categories. These are my two models. Categories Model import { Categories } from '../../categories/Mode/Categories' export class CategoryType { _id: strin ...

Chaining Assignments in TypeScript

let a: { m?: string }; let b = a = {}; b.m = ''; // Property 'm' does not exist on type '{}'. let a: { m?: string } = {}; let b = a; b.m = ''; // It's OK Link to TypeScript Playground What occurs ...

Developing Custom Methods with TypeScript Factories - Step 2

Working in a factory setting, I find myself needing to incorporate custom methods at some point. Thanks to the valuable input from the community, I've made progress towards my goal with this helpful answer, although I'm required to include a see ...