Reputation: 381
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);
Upvotes: 1
Views: 38
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.
Upvotes: 1