I want to define a routes array in React and i want to build a type that infers all possible route names
interface IRoute {
name?: string;
path: string;
component: any;
meta?: object;
children?: IRoute[];
}
const routes = [
{ name: "Home", path: "/", component: () => import(/* webpackChunkName: 'home_page' */ "../pages/Home") },
{ name: "About", path: "/about", component: () => import(/* webpackChunkName: 'about_page' */ "../pages/About") },
{
name: "Books",
path: "/books",
component: import(/* webpackChunkName: 'books_page' */ "../pages/Books"),
children: [
{ name: "Book", path: ":id", component: import(/* webpackChunkName: 'book_page' */ "../pages/Book") },
{ name: "NewBook", path: "new", component: import(/* webpackChunkName: 'new_book_page' */ "../pages/NewBook") },
],
},
] as const;
type RouteNames<T extends IRoute[], I extends number[] = []> =
I["length"] extends T["length"] ? never :
T[I["length"]]["name"] | (T[I["length"]]["children"] extends IRoute[] ? RouteNames<T[I["length"]]["children"]> : never) | RouteNames<T, [...I, I["length"]]>;
but i don't know why the type can't infer all possible values even nested ones
for example here i get only three first level names, but nested ones aren't there
const name: RouteNames<typeof routes> = 'About'
can anyone Suggest soemthing
thanks
the accepted solution worked perfect but under that <typeof routes>
i get a red line saying
import("/home/modex98/Desktop/react-hads-dirty/router/src/pages/Home")>; }, { readonly name: "About"; readonly path: "/about"; readonly component: () => Promise<...>; }, { ...; }]' does not satisfy the constraint 'readonly IRoute[]'.
Type '{ readonly name: "Home"; readonly path: "/"; readonly component: () => Promise<typeof import("/home/modex98/Desktop/react-hads-dirty/router/src/pages/Home")>; } | { readonly name: "About"; readonly path: "/about"; readonly component: () => Promise<...>; } | { ...; }' is not assignable to type 'IRoute'.
Type '{ readonly name: "Books"; readonly path: "/books"; readonly component: Promise<typeof import("/home/modex98/Desktop/react-hads-dirty/router/src/pages/Books")>; readonly children: readonly [{ ...; }, { ...; }]; }' is not assignable to type 'IRoute'.
Types of property 'children' are incompatible.
The type 'readonly [{ readonly name: "Book"; readonly path: ""; readonly component: Promise<typeof import("/home/modex98/Desktop/react-hads-dirty/router/src/pages/Book")>; readonly params: readonly ["id"]; }, { ...; }]' is 'readonly' and cannot be assigned to the mutable type 'IRoute[]'.ts(2344)
CodePudding user response:
If you are only interested in a union of all the name
values in routes
, a type like this should work:
type RouteNames<T extends readonly IRoute[]> =
T[number]["name"] | (T[number] extends infer U
? U extends { readonly children: infer C extends readonly any[] }
? RouteNames<C> & string
: never
: never)
type Names = RouteNames<typeof routes>
// type Names = "Home" | "About" | "Books" | "Book" | "NewBook"