type Route = ViewRoute | ChildRoute;
interface ViewRoute {
path: string;
name: string;
children?: readonly ChildRoute[];
}
interface ChildRoute extends ViewRoute {
isChild: true;
}
interface Options<R extends Route> {
routes: readonly R[];
}
type RouteNames<R extends Route> = R["name"] | (R extends { children: readonly Route[] } ? RouteNames<R["children"][number]> : never);
class RouteCollection<R extends Route> {
routes: readonly R[] = [];
constructor(options: Options<R>) {
this.routes = options.routes;
}
getRouteByName<N extends RouteNames<R>>(name: N): R | undefined {
const findRoute = (routes: readonly Route[]): R | undefined => {
for (const route of routes) {
if (route.name === name) {
return route as R;
}
if (route.children) {
const childMatch = findRoute(route.children);
if (childMatch) {
return childMatch;
}
}
}
return undefined;
};
return findRoute(this.routes);
}
}
const routes = [
{
path: "/",
name: "Home",
children: [
{
path: "/sub",
name: "SubHome",
isChild: true,
},
],
},
{
path: "/foo",
name: "Foo",
},
] as const;
const coll = new RouteCollection({ routes });
const home = coll.getRouteByName("Home");
const foo = coll.getRouteByName("Foo");
const subHome = coll.getRouteByName("SubHome");
console.log({home, foo, subHome})
output
{
"home": {
"path": "/",
"name": "Home",
"children": [
{
"path": "/sub",
"name": "SubHome",
"isChild": true
}
]
},
"foo": {
"path": "/foo",
"name": "Foo"
},
"subHome": {
"path": "/sub",
"name": "SubHome",
"isChild": true
}
}