The interconnected properties of nested objects in Typescript

Is it feasible to create a relationship between nested object properties in TypeScript?

For example:

type ActionFunction = (props: any) => ...
type NestedObject = {
   [key: string]: {
      action1: ActionFunction, params1: PARAMETERS OF "action1", 
      action2: ActionFunction, params2: PARAMETERS OF "action2" 
      ...
   }
}
const demo: NestedObject = {
   key1: {action1: (props: {name: string, age: number}) => ..., params1: HERE SHOULD BE TYPED},  
   
   key2: {action2: (props: {somethingRandom: number[]}) => ..., params2: HERE SHOULD BE TYPED},
   
   ... additional properties
}

Answer №1

In TypeScript, there isn't a direct type that matches the values you want Obj to accept. Instead, you can create a generic type called Obj<T>. This type acts as a constraint on T, ensuring that if T extends Obj<T>, then T is valid. You could also define a generic helper function asObj(obj) which checks if obj is a valid Obj<T> for some T.

To achieve this, you might write:

type Obj<T> = ⋯; 

and then something like:

const asObj = <T,>(obj: T & Obj<T>): T => obj;

However, a more realistic approach would be:

const asObj = <T,>(obj: T & Obj<T>): T => obj;

The question now is how to actually define Obj<T>.


You could define it in a manner similar to this. By testing each property of your targeted Obj type independently, you can structure it as a mapped type based on single-property checking:

type Obj<T> = { [K in keyof T]: F<T[K]> }

This leads us to the need to define F<>:

type F<T> =
   { [S in Suffixes as \`f${S}\`]: 
       (props: never) => void 
   } & { [S in Suffixes as \`args${S}\`]: 
       T extends Record<\`f${S}\`, (props: infer P) => void> ? P : never 
   }

type Suffixes = " | "1" | "2" // <-- you can adjust this based on your needs

This definition ensures that F<T> validates that T is a proper Obj property. It requires all expected f and args members, with the ability to modify the number via the Suffixes union type. The f, f1, f2, functions are designated to receive one argument respectively, and args, args1, args2, represent the valid argument types for their respective f functions.


Lets put this to the test:

const asObj = <T,>(obj: T & Obj<T>): T => obj;

const demo = asObj({
   key1: {
      f: (props: { name: string, age: number }) => { }, args: { age: 3, name: "" },
      f1: (props: { name: 2 }) => { }, args1: { name: 2 },
      f2: (props: unknown) => { }, args2: 2
   },
   key2: {
      f: (props: { x: boolean }) => { }, args: { x: true },
      f1: (props: { z: Date }) => { }, args1: { z: new Date() },
      f2: (props: number) => { }, args2: "oops" // error
   },
});

demo.key1.f(demo.key1.args); // okay
demo.key2.f1(demo.key2.args1); // okay

This code operates correctly by enforcing that each arg corresponds to an appropriate f method. Errors are flagged if any mistakes are found. Additionally, the compiler effectively remembers the arguments associated with each method through strong typing.

Access playground link here

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

Manipulating an Array of Objects based on conditions in Angular 8

I have received an array of objects from an API response and I need to create a function that manipulates the data by enabling or disabling a flag based on certain conditions. API Response const data = [ { "subfamily": "Hair ...

Using a function to reset a radio button selection in Ionic

Currently, I am in the process of developing an ionic app that includes a radio list on one of the pages. However, I have encountered an issue where the radio list does not clear when I navigate to the next screen and then return, resulting in a poor user ...

Utilizing Material-UI with MobileDialog HOC in TypeScript: A Beginner's Guide

I'm running into an issue while trying to implement withMobileDialog in my TypeScript code. Below is the snippet of my code, inspired by a code example from the official documentation. import withMobileDialog, { InjectedProps } from "@material-ui/co ...

Incompatibility issues between NestJS and socket.io package

After diligently following the documentation, I attempted to install and work with socket.io on nestjs. However, I encountered multiple issues when installing packages. Despite trying different methods to update the package, nothing seemed to resolve the i ...

Is there a way to define a variable of type Express in TypeScript without the need to import the express

One approach I am considering is to have a main.ts file where I import express just once using the following line of code: import express from 'express'; In another separate file, let's say a class where I want to define a method called "in ...

My React hooks are failing to properly update the state using useState

I have been encountering an issue with the code in my component where the state is not updating as expected. When I invoke the createUserList function, I can view the users in the UserList component. However, when I attempt to log the users in the UserCom ...

Converting JSON Arrays into Typed Arrays in NativeScript using Typescript

Can someone assist me with a problem I'm facing while learning NativeScript? I am trying to read a txt file that contains JSON data and then assign it to an Array called countries. However, despite my efforts, I have not been successful. The code sni ...

AutoAnimate animation customization is not compatible with React

I'm in the process of integrating a plugin from this source into my code. I've made adjustments to it for React, but it's still not working as expected. Can you assist me with resolving this issue? Custom Hook: import { useRef, useLayoutEff ...

Is it possible for a lambda in TypeScript to have access to the class scope but return undefined

In my TypeScript code, I have a class used as a Pipe in Angular 2 to render markdown. It compiles successfully but encounters a runtime exception on a specific line: var Remarkable = require('remarkable'); @Pipe({ name: 'markdown' ...

Variable Scope is not defined in the TypeScript controller class of an AngularJS directive

I have implemented a custom directive to wrap ag grid like so: function MyDirective(): ng.IDirective { var directive = <ng.IDirective>{ restrict: "E", template: '<div style="width: 100%; height: 400px;" ag-grid="vm.agGrid ...

Establish a connection to the ActiveMQ broker utilizing STOMP protocol in an Ionic application

I've recently received an Ionic + Capacitor app that is primarily meant to run on the Android platform. My current task is to incorporate communication with a remote ActiveMQ broker into the app. To achieve this, I utilized the STOMP JS library which ...

Accessing Nested FormGroup in Angular 6 by its name

Dealing with Nested Form Groups address = new FormGroup({ 'com.complex.Address':new FormGroup({ city: cityControl, streetName: streetNameControl, houseNumberAddition: houseNumberAdditionControl, ho ...

Tips for implementing a generic constant value in a TypeScript function

Is it permissible in TypeScript to have the following code snippet? function getFoo<P = "a"|"b">():string { // P represents a type, not an actual value! return "foo"; } getFoo<"a>">(); // no ...

What is the generic type that can be used for the arguments in

One function I've been working on is called time const time = <T>(fn: (...args: any[]) => Promise<T>, ...args: any[]): Promise<T> => { return new Promise(async (resolve, reject) => { const timer = setTimeout(() => r ...

"Implementing autocomplete feature with initial data in Angular 4 using FormControl

I have incorporated material.angular.io components into my app, particularly autocomplete. I am customizing it to function as a multi-select, but I am encountering an issue with loading initial values: export class CaseActivityTimeEditComponent implements ...

Creating nested return types: A guide to defining function return types within a Typescript class

My large classes contain functions that return complex objects which I am looking to refactor. class BigClass { ... getReferenceInfo(word: string): { isInReferenceList:boolean, referenceLabels:string[] } { ... } } I am considering somethi ...

Setting nodeIntegration to false led to an Uncaught ReferenceError: require is not defined when trying to access Object.url (external "url":1) in the electron-react-typescript environment

After setting nodeIntegration to false, I encountered the following error message: "Uncaught ReferenceError: require is not defined at Object.url (external 'url': 1)". https://i.sstatic.net/galzh.png Upon clicking on the link referring to "exte ...

Ensuring the proper typescript type for assigning a value in react-hook-form

I'm encountering an issue when trying to pass the function setValue() down to a child component. The error message I receive is: Type 'UseFormSetValue<Inputs>' is not assignable to type 'UseFormSetValue<Record<string, any> ...

Having trouble retrieving data from a json file using a GET request in Angular?

As a novice in dealing with json objects, I am having trouble extracting data from the GroupResult object. Below is the structure of my classes: export class GroupResult { Id!: number; Lecturer!: string; GroupName!: string; Subjects!: SubjectStats ...

Organize an array based on its ratio

I am attempting to organize an array based on the win and lose ratio of each player. This is how my code currently looks: const array = [{playerName: 'toto', win: 2, lose: 2}, {playerName: 'titi', win: 0, lose: 0}, {playerName: &apo ...