Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript: Define for JSON nested object

I have a nested json object and define a interface for it

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

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

But when I use it. It appear a warning.

const menuPermissionForKey = menuOnlyForRoles.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 did I do wrong or missing?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

You didn’t do anything wrong per se. TypeScript just cannot infer whether your menuPermissionForKey is a Menu object or a string array.

Ideally, you’d define your Menu type more strictly. Barring that, you can 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


Alternatively, if you’re dealing with static data that is known at compile-time, you can use a const assertion, optionally with the satisfies operator to enforce type-checking at edit time:

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading