I have react-router v6 in my app, and i want all paths in router config object to be absolute
But i need to ensure that path of child route of any depth begins with its parent path, otherwise app just might crash
Seems like i need a recursive type with template literals
Type i've come with (just path and children to simplify):
type Route<T extends string> = {
path: T;
children?: Route<`${T}/${string}`>[];
};
But it doesn't work for nested routes, T
generic becomes of string
type
const invalidRoute: Route<"/home"> = {
path: "/home",
children: [
{
path: "/home/about",
children: [
{
// type `/home/${string}/${string}` here, type `/home/about/${string}` expected
// so no typescript error here is raised
path: "/home/nonabout/whatever",
},
],
},
],
}
Could you help?
Unfortunately, you can't do this in typescript. Your Route type itself is saying the child can be anything, so of course when you call the type recursively, T becomes /home/${string}
. Because this really matters on instantiation, I would suggest having a function which validates your Route and throws if it isn't valid.
type Route = {
path: string;
children?: Route[];
};
const route: Route = {
path: "/home",
children: [
{
path: "/home/about",
children: [
{
path: "/home/about/whatever1",
},
{
path: "/home/about/whatever2",
},
],
},
{
path: "/home/something",
children: [
{
path: "/home/notSomething/whatever1",
},
{
path: "/home/something/whatever2",
},
],
},
],
} as const;
/**
* Validates the react router route object contains paths which are absolute
* @throws TypeError when child path is not prefixed with parent path
*/
function enforceAbsoluteRoutes(route: Route): void {
const parentPath = route.path;
if (route.children) {
for (const child of route.children) {
if (!child.path.startsWith(parentPath)) {
throw new TypeError(
`Invalid path (${child.path}) is not prefixed with parent path (${parentPath})`
);
}
enforceAbsoluteRoutes(child);
}
}
}
enforceAbsoluteRoutes(route);
I had a similar problem where I wanted to convert an object to the string dot notation. I had to define a depth, start at the farthest child and work up the tree to create my string. However, this would only work if all your paths had the same children depth. This is incredibly complex and would not work for your situation.