John Chucks
John Chucks

Reputation: 381

Defining data types for the recursive data is not correctly worked

I'm facing a problem in defining the proper types for the data. I have an array of objects which have the following data and my task is to give proper types according to the structure of the data.

export const DashboardSections = [
    {
        name: 'Dashboard',
        sectionItems: [
            {
                title: 'Dashboard',
                icon: 'FaHome',
                path: adminUrl,
                type: 'link',
                active: true,
                children: [],
            },
            {
                title: 'Products',
                icon: 'FaBox',
                path: `${adminUrl}/products`,
                type: 'sub',
                active: false,
                children: [
                    {
                        path: `${adminUrl}/products`,
                        title: 'Products',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                    {
                        path: `${adminUrl}/categories`,
                        title: 'Categories',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                    // Tags are more specific (describe your posts in more detail)
                    {
                        path: `${adminUrl}/tags`,
                        title: 'Tags',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                    {
                        path: `${adminUrl}/attributes`,
                        title: 'Attributes',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                ],
            },
            {
                title: 'Menus',
                icon: 'FaThLarge',
                path: `${adminUrl}/menus`,
                type: 'link',
                active: false,
                children: [],
            },
        ],
    },
    {
        name: 'System',
        sectionItems: [
            {
                title: 'Appearance',
                icon: 'FaPalette',
                path: `${adminUrl}/sliders`,
                type: 'sub',
                active: false,
                children: [],
            },
        ],
    },
];

I think the data after the root item has a recursive structure. So I have defined types like this.

type link = 'link' | 'sub';

type rootDashItem = {
    /** Title of the root item */
    title: string;
    /** Only root item could contain a svg **/
    icon: string;
    /** Path of the root item */
    path: string;
    /** Root item could be a 'link' i.e. don't have any children(empty array) or 'sub' means has array of children. */
    type: link;
    /** Item would be active or not */
    active: boolean;
    /** Contain array of sub items */
    children: subDashItem[];
};


type subDashItem = {
    title: string;
    path: string;
    type: link;
    active: boolean;
    children: subDashItem[];
};


export type dashSection = {
    name: string;
    sectionItems: rootDashItem[];
};

But when I try to reuse the type in somewhere else

const items = DashboardSections.map((section: dashSection ) => {
     return section.sectionItems.filter((item: rootDashItem) => {
        if (item.active) {
            return {...item, section: section.name}
        }
    });

Linter is throwing an error on this line DashboardSections.map((section: dashSection ).

Error:(130, 37) TS2345: Argument of type '(section: dashSection) => rootDashItem[]' is not assignable to parameter of type '(value: { name: string; sectionItems: { title: string; icon: IconType; path: string; type: string; active: boolean; children: { path: string; title: string; type: string; active: boolean; children: never[]; }[]; }[]; }, index: number, array: { ...; }[]) => rootDashItem[]'.
  Types of parameters 'section' and 'value' are incompatible.
    Type '{ name: string; sectionItems: { title: string; icon: IconType; path: string; type: string; active: boolean; children: { path: string; title: string; type: string; active: boolean; children: never[]; }[]; }[]; }' is not assignable to type 'dashSection'.
      Types of property 'sectionItems' are incompatible.
        Type '{ title: string; icon: IconType; path: string; type: string; active: boolean; children: { path: string; title: string; type: string; active: boolean; children: never[]; }[]; }[]' is not assignable to type 'rootDashItem[]'.
          Type '{ title: string; icon: IconType; path: string; type: string; active: boolean; children: { path: string; title: string; type: string; active: boolean; children: never[]; }[]; }' is not assignable to type 'rootDashItem'.
            Types of property 'type' are incompatible.
              Type 'string' is not assignable to type 'link'.

What I'm doing wrong? What is a better way to define types for the recursive structure?

Full Code:

const adminUrl = "http://localhost:3000/admin";


//#region Types

type link = 'link' | 'sub';

type subDashItem = {
    title: string;
    path: string;
    type: link;
    active: boolean;
    children: subDashItem[];
};



type rootDashItem = {
    /** Title of the root item */
    title: string;
    /** Only root item could contain a svg **/
    icon: string;
    /** Path of the root item */
    path: string;
    /** Root item could be a 'link' i.e. don't have any children(empty array) or 'sub' means has array of children. */
    type: link;
    /** Item would be active or not */
    active: boolean;
    /** Contain array of sub items */
    children: subDashItem[];
};




export type dashSection = {
    name: string;
    sectionItems: rootDashItem[];
};
//#endregion Types


//#region Item Structure
export const DashboardSections = [
    {
        name: 'Dashboard',
        sectionItems: [
            {
                title: 'Dashboard',
                icon: '',
                path: adminUrl,
                type: 'link',
                active: true,
                children: [],
            },
            {
                title: 'Products',
                icon: '',
                path: `${adminUrl}/products`,
                type: 'sub',
                active: false,
                children: [
                    {
                        path: `${adminUrl}/products`,
                        title: 'Products',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                    {
                        path: `${adminUrl}/categories`,
                        title: 'Categories',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                    // Tags are more specific (describe your posts in more detail)
                    {
                        path: `${adminUrl}/tags`,
                        title: 'Tags',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                    {
                        path: `${adminUrl}/attributes`,
                        title: 'Attributes',
                        type: 'link',
                        active: false,
                        children: [],
                    },
                ],
            },
            {
                title: 'Menus',
                icon: '',
                path: `${adminUrl}/menus`,
                type: 'link',
                active: false,
                children: [],
            },
        ],
    },
    {
        name: 'System',
        sectionItems: [
            {
                title: 'Appearance',
                icon: '',
                path: `${adminUrl}/sliders`,
                type: 'sub',
                active: false,
                children: [],
            },
        ],
    },
];

//#endregion Item Structure

// Challenge is to take those root items which are active with its corresponding section title
const items = DashboardSections.map((section: dashSection ) => {
     return section.sectionItems.filter((item: rootDashItem) => {
        if (item.active) {
            return {...item, section: section.name}
        }
    });

});

console.log(items);

Live link

Upvotes: 1

Views: 38

Answers (1)

Derrick Beining
Derrick Beining

Reputation: 894

You just need to annotate the type of DashboardSections:

export const DashboardSections: Array<dashSection> = [/*...*/]

And then fix the filter callback in section.sectionItems.filter((item: rootDashItem) => .... The filter function should return a boolean. So you probably want to filter by item.active and then map again to transform the item as needed.

Here's the code

Upvotes: 1

Related Questions