Converting a dynamic JSON object into a generic type in TypeScript

I need assistance with converting a JSON object into the equivalent generic type in TypeScript.

The JSON object I have contains dynamic keys such as applications and permissions. The keys inside applications, like application_management and user_management, can also be dynamic. Similarly, the keys inside permissions, such as create_application, delete_application, update_application, etc., can be dynamic as well.

I attempted to accomplish this using an interface but unfortunately faced challenges. Could someone please help me resolve this issue?

{
            "id": 1,
            "isSuperAdmin": true,
            "firstName": "Oleksii",
            "lastName": & "Michael",
            "email": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="452429203d3505352a2c2b31773220276b262a28">[email protected]</a>",
            "gender": "male"",
            "dob": "1990-01-01",
            "photo": null,
            "status": "ACTIVE",
            "roles": [
                {
                    "id": 1,
                    "slug": "Head of Department-(Account Management-Network-P2W North America)"
                },
                {
                    "id": 2,
                    "slug": "Team Lead-(Account Management-Network-P2W North America)"
                },
                {
                    "id": 3,
                    "slug": "Employee-(Account Management-Network-P2W North America)"
                }
            ],
            "applications": {
                "application_management": {
                    "id": 41,
                    "slug": "application_management",
                    "appName": "Application Management",
                    "status": "ACTIVE",
                    "appType": "CODED",
                    "appUrl": "application-management",
                    "appIcon": "http://api.chromosome-studio.com/uploads/applications/application.png",
                    "permissions": {
                        "delete_application": {
                            "id": 3,
                            "action": "Delete Application",
                            "slug": "delete_application",
                            "level": "all"
                        },
                        "update_application": {
                            "id": 2,
                            "action": "Update Application",
                            "slug": "update_application",
                            "level": "all"
                        },
                        "create_application": {
                            "id": 1,
                            "action": "Create Application",
                            "slug": "create_application",
                            "level": "all"
                        }
                    }
                },
                


                "user_management": {
                    "id": 42,
                    "slug": "user_management",
                    "appName": "User Management",
                    "status": "ACTIVE",
                    "appType": "CODED",
                    "appUrl": "user-management",
                    "appIcon": "http://api.chromosome-studio.com/uploads/applications/users.png",
                    "permissions": {
                        "create_user": {
                            "id": 4,
                            "action": "Create User",
                            "slug": "create_user",
                            "level": "all"
                        }
                    }
                }
            }

Answer №1

Oh no, another Romain beat me to it. However, my solution offers a slight variation

type Role = { id: number; slug: string };
type ObjectStatus = 'ACTIVE' | 'INACTIVE'; 
type AppType = 'CODED' | 'DYNAMIC';
type PermissionLevel = 'all' | 'admin' | 'guest'; 

type ApplicationsType = 'user' | 'application';
type CrudTypes = 'delete' | 'update' | 'create';
type PermissionKeys = `${CrudTypes}_application`;

type Permission<NAME extends ApplicationsType, CRUD extends CrudTypes = CrudTypes> = {
    id: number;
    action: `${Capitalize<CRUD>} ${Capitalize<NAME>}`;
    slug: `${CRUD}_${NAME}`;
    level: PermissionLevel;
};

type Application<NAME extends ApplicationsType> = {
    id: number;
    slug: `${NAME}_management`;
    appName: `${Capitalize<NAME>} Management`;
    status: ObjectStatus;
    appType: AppType;
    appUrl: `${NAME}-management`;
    permissions: { [key in PermissionKeys]?: Permission<NAME> };

    [key: string]: unknown;
};

type ApplicationList = { [key in ApplicationsType as `${key}_management`]?: Application<key> };

type User = {
    id: 1;
    isSuperAdmin: true;
    firstName: string;
    lastName: string;
    email: string;
    gender: string | null;
    dob: string | null;
    photo: string | null;
    status: ObjectStatus;
    roles: Array<Role>;
    applications: ApplicationList;
};

Upon applying this type to your input, I encountered compilation errors on

  • application.user_management.appName, which should be 'User Management'
  • application.user_management.permission.create_application.action, expected to be 'Create User'
  • application.user_management.permission.create_application.slug, anticipated to be 'create_user'

A minor update was made to refine the typing of permission list due to imperfect typing issues

type PermissionList<NAME extends ApplicationsType> = { [key in CrudTypes as `${key}_${NAME}`]?: Permission<NAME, key> };

Then make the replacement in Application :

permissions: PermissionList<NAME>;

This resulted in an additional error on

  • application.user_management.permission.create_application, now expected to be 'create_user', allowing only 'Create' as a crud action.

playground

Answer №2

Below is a defined type for your JSON object:

    type JsonObjectType = {
        id: number;
        isAdmin: boolean;
        firstName: string;
        lastName: string;
        email: string;
        gender: string | null;
        dob: string | null;
        photo: string | null;
        status: "ACTIVE";
        roles: {
            id: number;
            slug: string;
        }[];
        applications: Record<string, {
            id: number;
            slug: string;
            appName: string;
            status: "ACTIVE";
            appType: "CODED";
            appUrl: string;
            appIcon: string;
            permissions: Record<string, {
                id: number;
                action: string;
                slug: string;
                level: "all";
            }>;
        }>
    };

The usage of Record<KeyType,ValueType> allows for dynamic properties with string keys and complex typed values.

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

How can I retrieve the decimal x and y coordinates when the mouse is moved in Typescript Angular?

I am in the process of transitioning my user interface from Flash/Flex, where it stores values in decimal format. I need to access and reuse these values. Here is a demo showcasing my problem. Here is a snippet: import { Component, Input } from '@an ...

What is the best way to access buffer data in TypeScript for Solana?

Is there a way to retrieve buffer data from TypeScript? I am attempting to use the public key to access all of my token lists, but I am only getting back an empty array of objects. import {Connection, Keypair} from "@solana/web3.js"; const Sola ...

Converting JSON data into TypeScript interface objects within an Angular 2 environment

I have a JSON dataset with the following structure: { "timestamp": 1467471622, "base": "USD", "rates": { "AED": 3.673027, "AFN": 68.475, "ALL": 123.095199, "AMD": 476.8075, "ANG": 1.78385, "AOA": 165.846832, "ARS": 15.05 ...

What is the best way to clear all content from the "textarea" and input fields after submitting?

I'm currently using a Devextreme library for my project. I am having trouble finding a way to clear all the textarea information in the component along with other inputs when clicking on the Save button. Despite trying various methods, I have not bee ...

When executing the release command in Ionic 3, the Angular AoT build encountered a failure

Struggling to get my Sony Z2 smartphone app running. Command used: ionic build android --prod --release Error displayed in console: typescript error Type CirckelmovementPage in C:/Users/fearcoder/Documents/natuurkundeformules/src/pages/cir ...

Unable to retrieve query within async function, unable to import graphql queries externally

Is there a way to fetch characters from the parent component when a property changes and utilize these props? I attempted to use the useQuery function within a method and execute this method on prop change, but it seems like something is not functioning co ...

The 'src' properties in nextjs/image are of different types and therefore cannot be used interchangeably

I'm currently using React Dropzone to upload multiple images in my basic application. To display the types of images that are being dropped, I created a separate component with TypeScript. However, Next.js is throwing an error when it comes to the ima ...

Ionic 2 Media Plugin File Status with Ionic Native

In the Ionic Native Media plugin documentation found here, it mentions that there are both static and instance members, such as status. I am looking for an example related to the file status in the media plugin. I attempted to do this: console.log(this. ...

Transform JSON object to a class/interface object using Typescript

I am currently working on converting an API response into a TypeScript class or interface. The API is returning a list of objects with various properties, but I only require a few specific properties from the response object. Example of API Response: ...

Encountering a TypeScript error while calling a Vue lifecycle hook method

Struggling to call a method in a Vue root component from a lifecycle method in typescript? See below for a simple example that showcases this issue: import Vue from "vue"; class Game { a: number; b: number; constructor() { this.a = 3; ...

What is the proper syntax for defining an object property as a function?

Could someone help me find the specific location in the documentation where I can learn about the syntax of the line testMessage: (): string => {? Shouldn't it be more like testMessage: () => string;? Why are they not the same? export default { ...

Implementing an Asynchronous Limited Queue in JavaScript/TypeScript with async/await

Trying to grasp the concept of async/await, I am faced with the following code snippet: class AsyncQueue<T> { queue = Array<T>() maxSize = 1 async enqueue(x: T) { if (this.queue.length > this.maxSize) { // B ...

Encountering compilation errors while using ng serve in NGCC

After attempting to update peer dependencies, I encountered an issue while compiling my Angular app. The error message displayed: Compiling @angular/material/core : es2015 as esm2015 Compiling @angular/material/expansion : es2015 as esm2015 Compiling @angu ...

Encountering an issue in the test file when using react-router-dom v6: "The 'history' property is not found on the 'IntrinsicAttributes & RouterProps' type."

Main script: import { useContext, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import AuthenticationContext from './AuthenticationContext'; function HandleOAuthCallbackRoute() { co ...

Guide on Implementing Link href in Next.js v12

When I set the href as a string, the link functions properly. However, when I use an object for the href, the link fails to work. Despite seeing the correct querystring when hovering over the link, it always takes me back to the first page. // ProdCard t ...

The unexpected identifier 'express' was encountered in the import call, which requires either one or two arguments

I'm in the process of constructing an express server using typescript and Bun. Recently, I completed my register route: import express from "express"; const router = express.Router(); router.get('/registerUser',(_req:express.Reque ...

Why does the implementation of my interface differ from what is specified in the TypeScript documentation?

Currently delving into the world of TypeScript documentation https://www.typescriptlang.org/docs/handbook/2/classes.html Specifically focusing on the section implements Clauses, an interesting revelation surfaces: A Word of Caution It’s worth noting t ...

Create a list of items with checkboxes next to each that can be repeated using PdfMake

Incorporating pdfMake into my project, I am trying to display text next to an image and replicate this section in my docDefinition. The issue arises when I attempt to repeat this part using the following code snippet: { columns: [ { ...

Navigating to a component route in Angular

I need to retrieve the routing URL of a component that is different from the current URL and save it in a service. For example, if my component is called parentComponent and contains another component called childComponent, then the URL of the child compon ...

Http' does not have the 'update' property

I recently implemented Angular 2 Release and utilized 'Http' from '@angular/http' for my project. However, I encountered an error when I invoked the method 'update', which resulted in the following message: "Evidently, th ...