Creating a structure for a nested JSON object in TypeScript

I'm working with a nested JSON object and defining an interface for it.

interface Menu {
    [key: string]: string[] | Menu;
}

const menuRoles: Menu = {
    site: {
        only: [],
        category: {
            only: ['manager', 'head', 'lead'],
        },
        'floor-section': {
            only: ['head', 'lead'],
        },
    },
};

However, when I try to use it, a warning appears.

const menuPermissionForKey = menuRoles.site || { only: [] };
const rolesCanAccessMenu = menuPermissionForKey.only || [];
                                               ▔▔▔▔
     any
     Property 'only' does not exist on type 'Menu | string[]'.
       Property 'only' does not exist on type 'string[]'

What am I doing wrong or what am I missing?

Answer №1

There is no specific mistake on your part. TypeScript simply cannot automatically determine whether your menuPermissionForKey variable points to a Menu object or a string array.

It would be ideal if you could define the structure of your Menu type more precisely. In the absence of that, you have the option to create a type predicate:

interface Menu {
    [key: string]: string[] | Menu;
}

const menuOnlyForRoles: Menu = {
    site: {
        only: [],
        category: {
            only: ['manager', 'head', 'lead'],
        },
        'floor-section': {
            only: ['head', 'lead'],
        },
    },
};

function isMenu(data: Menu | string[]): data is Menu {
    return !Array.isArray(data);
}

const menuPermissionForKey = menuOnlyForRoles.site || { only: [] };
const rolesCanAccessMenu= isMenu(menuPermissionForKey)
                              ? menuPermissionForKey.only || []
                              : [];

Playground link


If you are working with static data that is known at compile time, another approach is to utilize a const assertion, potentially incorporating the satisfies operator for stricter type checking during editing:

interface Menu {
    [key: string]: readonly string[] | Menu;
}

const menuOnlyForRoles = {
    site: {
        only: [],
        category: {
            only: ['manager', 'head', 'lead'],
        },
        'floor-section': {
            only: ['head', 'lead'],
        },
    },
} as const satisfies Menu;

const menuPermissionForKey = menuOnlyForRoles.site || { only: [] };
const rolesCanAccessMenu = menuPermissionForKey.only || [];

Playground link

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

Selenium encountered an error when trying to execute the 'querySelector' function on the document. The selector provided, my_selector, is not recognized as a valid selector

Whenever I run this code: document.querySelector(my_selector) using selenium, an error is thrown: Failed to execute 'querySelector' on 'Document' my_selector is not a valid selector my_selector is definitely a valid selector that func ...

Controller experiencing peculiar AJAX response in CodeIgniter

I recently embarked on a Codeigniter project and now I'm faced with the task of making an AJAX call to a specific controller. Here is the scenario: - I have two dropdown menus: one for selecting counties and the other should populate with cities with ...

"Resizing issue with multiple Highcharts not being resolved after selecting a new option from

Is there a way to resize multiple Highcharts on a webpage by clicking a button? When attempting to update the last chart using a dropdown menu and then clicking the resize button, an error message stating Uncaught TypeError: Cannot read property 'refl ...

What is the best way to handle variables in javascript?

After making an AJAX request, I expected the results to display when I select from the drop down menu. However, I encountered a problem where only a table is being displayed. I am confused as to why the data is not coming from the variables $grade (calcula ...

Utilizing unique background images tailored to different screen resolutions

In order to enhance the user experience on my website, I am looking to implement a feature that dynamically changes the background images based on the user's screen resolution. My plan is to use a small snippet of JavaScript within the <head> s ...

Adding HTML elements or content dynamically in Angular can be achieved using various methods and techniques

Is there a way to dynamically insert an additional column into an HTML table when a button is clicked? I am looking to have a button below the table that, when clicked, will add the same number of rows currently in the table and introduce a new column. I ...

The div element fails to display even after invoking a JavaScript function to make it visible

As a newcomer to JavaScript & jQuery, please bear with me as I ask a very basic question: I have created the following div that I want to display when a specific JavaScript function is called: <div id='faix' class="bg4 w460 h430 tac poa ...

I'm puzzled as to why I keep receiving unexpected months despite utilizing the BETWEEN method in my query

It just dawned on me that I mistakenly posted the wrong code for my question earlier Despite setting the BETWEEN method in my statement, I am still getting random months when trying to run it in my nodejs with mysql2. Why is that happening? SELECT u.d ...

Ensure that the Jquery inview element functions both upon initial load and as the user scrolls

I've been working on implementing the inview function by adding and removing a class to an element, but for some reason it's not functioning as expected. Can anyone offer some assistance with this? http://jsfiddle.net/zefjh/ $.fn.isOnScreen = f ...

What is causing the issue with transmitting the server datetime to my local jQuery script?

I am facing an issue with my timeoftheserver.php page setup. The PHP code is quite simple: <?php echo date('D, d M y H:i:s'); ?> Similarly, the script on my local machine is also straightforward: var today; try { today = new Date($. ...

How to use jQuery to select nested elements in ASP.NET?

I'm struggling to understand why this selector is correctly targeting the txtUsername element: aspx: <asp:Content ID="Content1" ContentPlaceHolderID="body" runat="Server"> ... <div class="copy" style="float: left; width: 210px"> ...

The table is not being queried for alphabet letters

I'm having an issue with my table that has a column called BarcodeID. The numerical values work fine and I get the correct output, but when it comes to alphabet letters, it's not working as expected. I keep getting an error message: Unknown co ...

JavaScript escape sequences are used to represent characters that are

Is there a way to pass a variable to another function in this scenario? I am inserting a textarea using JavaScript with single quotes, but when the function myFunction(abc123) is called, it appears like this, whereas it should look like myFunction('ab ...

What is the best way to determine the file size using Node.js?

My current challenge involves using multer for uploading images and documents with a restriction on file size, specifically limiting uploads to files under 2MB. I have attempted to find the size of the file or document using the code below, but it is not p ...

Is it possible to dynamically insert a ng-mouseover in AngularJS using Javascript?

It seems like there is an issue with this code: var run_div = document.createElement('div'); run_div.className = 'whatever'; run_div.textContent = 'whatever'; run_div.setAttribute('ng-mouseover', 'console.log(&b ...

Using Webpack postcss prefixer with Vue CLI 3

As I work on implementing Bulma CSS in my project using Vue CLI 3, I encounter the need to prefix the classes with webpack. While I found an example of this process, adapting it from a webpack config to vue.config.js poses some challenges. Here is the ini ...

What is the method to turn off local package dependency when using `npm install --prefix`?

I am facing an issue with my project structure, where I have a main package.json file at the root level: Main package.json: { "name": "parent-project", "dependencies": { ... } } In a subfolder named child-project, ...

Is it possible to seamlessly alternate between JSON using the Jackson processor and XML using XStream, or is it feasible to use both formats simultaneously

I am in the process of developing a Web Server that can convert an Object into both JSON and XML formats. I have successfully used Jackson to serialize an object into JSON through my REST Interface, but I also need to support XML serialization. Recently, ...

Outputting an object using console.log in Node.js

When I print the error object in nodejs, the result of console.log(err) appears as follows: { [error: column "pkvalue" does not exist] name: 'error', length: 96, severity: 'ERROR'} I'm curious about the information enclosed ...

I often find myself feeling unsure when I incorporate conditional logic in JSX within Next.js

Hello, I am currently using Next.js and encountering an issue with using if/else in JSX. When I use if conditions, the classes of elements do not load correctly. Here is my code: <Nav> { login ? ...