Home > database >  Type doesn't infer nested key type
Type doesn't infer nested key type

Time:09-05

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"

Playground

  • Related