Nesting objects within arrays using Typescript Generics

Hello, I am currently attempting to assign the correct type to an object with nested values.

Here is a link to the code in the sandbox: https://codesandbox.io/s/0tftsf

interface Product {
  name: string,
  id: number
  productList?:ProductItem[]
}

interface ProductItem { 
  color: string, 
  size: number 
 }


type IValidation<T> = {
  field: keyof T
  nestedValidations?: IValidation<
    Pick<
      T,
      {
        [K in keyof T]-?: T[K] extends object ? K : never
      }[keyof T]
      >
    >[] // THIS IS CRITICAL FOR MY QUESTION!
  validators?: (any | any | any)[]
}

export async function validateIt<T>(payload: T, validations: IValidation<T>[]): Promise<
  Partial<{
    [key in keyof T]: string[]
  }>
  > { 
    return Promise.resolve(payload);
  }

const product: Product = {
  id: 1,
  name: 'playstation',
  productList: [{
    color: 'red',
    size: 32
    }
  ]
}

const test = validateIt<Product>(product, [
  {
    field: "productList",
    validators: [],
    nestedValidations: [
      {
        field: 'color',
        validators: []
      }
    ]
  }
])

I am encountering a type error and trying to determine the correct type for the nestedValidations property, which should align with the interface Product.

https://i.sstatic.net/FayYJ.png

Answer №1

Using Typescript Version 3.7 and Above

To accomplish this task, you can utilize the in keyword along with keyof. Essentially, you will be "generating" all potential types for each key, and TypeScript will identify the matching one.

type IValidation<T> = T extends Array<infer R> ? IValidation<R> : T extends object ? {
    [K in keyof T]: {
        field: K
        nestedValidations?: IValidation<T[K]>[]
        validators?: (any | any | any)[]
    }
}[keyof T] : never

Try it out here


This approach is not compatible with older versions of TypeScript as they do not support recursive types. More information available here.

Answer №2

Perhaps I don't have the full perspective, but it seems like you are aiming to accomplish this:

interface Product {
  name: string,
  id: number
}

type ValueOf<T> = T[keyof T]; // unnecessary

type IValidation<T> = {
  field: keyof T
  nestedValidations?: IValidation<T>[] // THIS IS CRUCIAL FOR THE QUESTION!
  validators?: (any | any | any)[]
}

export async function validateIt<T>(payload: T, validations: IValidation<T>[]): Promise<
  Partial<{
    [key in keyof T]: string[]
  }>
  > { 
    return Promise.resolve(payload);
  }

const product: Product = {
  id: 1,
  name: 'playstation'
}

const test = validateIt<Product>(product, [
  {
    field: "id",
    validators: [],
    nestedValidations: [
      {
        field: "name",
        validators: []
      }
    ]
  }
])

Experiment here: Typescript Playground

Please bear in mind that I needed to simplify your data structure as I am not familiar with how 'IRequiredValidator' is defined.

Essentially, we transition from

nestedValidations?: IValidation<ValueOf<T>>[]

to

nestedValidations?: IValidation<T>[]

because you intend to extract the keys of T and not the keys of (keys of T). This way, it resolves to all properties of the type assigned to a property of T.

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

Are all components in Next.js considered client components by default?

I have created a Next.js app using the app folder and integrated the Next Auth library. To ensure that each page has access to the session, I decided to wrap the entire application in a SessionProvider. However, this led to the necessity of adding the &apo ...

Error: webpack is failing to load the style and CSS loaders

I'm currently experimenting with the FullCalendar plugin from fullcalendar.io. They recommended using Webpack as a build system, which is new to me. I managed to set up the calendar functionality after some research, but I'm facing issues with th ...

Creating a universal parent constructor that can take in an object with keys specific to each child class

I am looking to create a base class with a constructor that allows for all the keys of the child class to be passed. However, I am facing a challenge because 'this' is not available in constructors. Here is what I hope to accomplish: class BaseCl ...

What is the best way to swap out every instance of an array?

There are two arrays that I'm working with, The first array is defined as var symbols = ['A', 'B'];, and the second array is defined as var num = ['3', 'A', '5', '4']; I am looking for a so ...

Tips for extracting specific JSON response data from an array in TypeScript

I have an array named ReservationResponse, which represents a successful response retrieved from an API call. The code snippet below demonstrates how it is fetched: const ReservationResponse = await this.service.getReservation(this.username.value); The st ...

Exploring the concept of multiple inheritance using ES6 classes

My research on this topic has been primarily focused on BabelJS and MDN. However, I acknowledge that there may be more information out there regarding the ES6 Spec that I have not yet explored. I am curious about whether ES6 supports multiple inheritance ...

Leveraging the power of LocalStorage in Ionic 2

Trying to collect data from two text fields and store it using LocalStorage has proven tricky. Below is the code I have set up, but unfortunately it's not functioning as expected. Can you provide guidance on how to resolve this issue? In page1.html ...

Employing state management in React to toggle the sidebar

A working example of a sidebar that can be toggled to open/close using CSS, HTML and JavaScript is available. Link to the Example The goal is to convert this example to React by utilizing states instead of adding/removing CSS classes. To ensure the side ...

How you can fix the issue of the "Element not visible" error occurring specifically for one element within the popup

Error Message: "Element Not Visible" when Script Reaches Last Element Despite all attributes being in the same form, an error occurs when the script reaches the last element and displays "element not visible." All elements are enclosed in div tags on the ...

Implementing AngularJS JQuery Datatables Directive for Displaying Multiple Data Tables within a Single View

I have successfully implemented the following directive: angular.module('myApp').directive('jqdatatable', function () { return { restrict: 'A', link: function (scope, element, attrs, ngModelCtrl) { ...

Prevent a React component from unnecessarily re-rendering after a property has been set

I am working on a react component that displays a streaming page similar to the one shown in this image. Here is a snippet of the code : const [currentStream, setCurrentStream] = useState<IStream>(); const [currentStreams] = useCollectionData<ISt ...

Return to a mention of a tree reference

Here is an example code snippet: <table> <tr> <td></td> <td></td> <td> <table> <tr> <td><a href="#" class="fav">click me</a></td> ...

Please input the number backwards into the designated text field

In my react-native application, I have a TextInput where I need to enter numbers in a specific order such as 0.00 => 0.01 => 0.12 => 1.23 => 12.34 => 123.45 and so on with each text change. I tried using CSS Direction "rtl" but it didn' ...

Error: The window object is not found in this context when initializing the Commerce module from the commerce.export.js file located in the node_modules folder within the @chec

I have been working with the pages router and custom app file (_app.js) https://nextjs.org/docs/pages/building-your-application/routing/custom-app It seems that the issue may be within my _app.js file, where I have the following code: import '../glo ...

A step-by-step guide on creating a chainable command in Cypress

Imagine having a variable called username. Now, consider a chainable function that needs to verify whether the username is empty or not. Original Method: if(username !== "") { cy.get('#username').type(username) } Expected Outcome: ...

A guide to managing Ajax in functional components in React without using classes

Currently, I am striving to develop all my components as pure functions. However, I have encountered an issue. The component I am working on resembles the structure below. The problem arises when the result of an ajax request triggers a rerender, leading ...

Enhance hover effects with JQuery through dynamic mouse movements

$(document).ready(function() { $(".hoverimage").hover( function(e) { updateCoords(this,e); openQuicktip(this); }, function() { closeQuicktip(); } ); $("area").hover( function(e) { updateCoords(this,e); openQuicktip(this); }, function ...

Are strings in an array being truncated by Firebug console log?

I have a unique function for logging messages to the firebug console that I'd like to share: // Just having fun with names here function ninjaConsoleLog() { var slicer = Array.prototype.slice; var args = slicer.call(arguments); console.lo ...

Error encountered: MongoDB cast exception - nested subdocument

Check out this schema design: var messageSchema = new Schema({ receivers: [User], message: String, owner: { type: Schema.Types.ObjectId, ref: 'User' } }); var userSchema = new Schema({ name: String, photo: String }); var in ...

Is there a way to access an object within another object without the need to use a function

Having trouble accessing an object? Let's solve this mystery together. I'm trying to access 'ctx', but base.ctx keeps returning null (is there a closure involved?). window.base = function () { var c = null, ctx = null; ...