What is the best way to define recursive Routes type using absolute paths?

I am using react-router v6 in my application, and I need all paths in the router config object to be absolute

It is crucial for the path of a child route at any depth to start with its parent path to avoid potential crashes

It appears that a recursive type with template literals may be necessary

The type I have devised (with only path and children for simplicity) is as follows:

type Route<T extends string> = {
  path: T;
  children?: Route<`${T}/${string}`>[];
};

However, this approach does not work for nested routes, where the generic T reverts to a string type

const invalidRoute: Route<"/home"> = {
  path: "/home",
  children: [
    {
      path: "/home/about",
      children: [
        {
          // Expected type `/home/${string}/${string}` here, got `/home/about/${string}`
          // This lack of TypeScript error makes it challenging
          path: "/home/nonabout/whatever", 
        },
      ],
    },
  ],
}

Any assistance would be greatly appreciated!

Answer №1

Regrettably, accomplishing this task in typescript is not feasible. The Route type itself allows for any child element, so when the type is called recursively, T transforms into /home/${string}. Due to its importance at instantiation, it is advisable to incorporate a function that verifies your Route and raises an error if it does not meet the criteria.

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 ensureAbsoluteRoutes(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);
    }
  }
}

ensureAbsoluteRoutes(route);

I encountered a similar issue where I needed to convert an object into dot notation strings. To solve this, I had to define a depth, starting from the deepest child and moving upwards through the tree to generate the desired string. However, this approach only works efficiently if all paths have the same depth of children. This method is extremely intricate and may not be suitable for your specific scenario.

Typescript Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Automatically create index.d.ts type definitions from a TypeScript module with just a few clicks

If I have a TypeScript module named my-function.ts with the following code : export function myFunction (param: number): number { return param } When this module is compiled to JavaScript, it loses its type definitions. Is there a way to automatically ge ...

Utilizing formData.append in TypeScript to handle arrays

Hey there! I'm facing an issue while trying to send a form to my Profile endpoint. The problem lies in the 'user:{}' field, as I am unable to properly insert my array data into this specific field. Here is a breakdown of the fields within m ...

Exploring TypeScript with personalized iterators

Can anyone help me figure out how to create an iterator for my array in TypeScript without encountering a transpilation error? Is it possible using generics or some other method? let array4 = [10, 20, 30]; array4[Symbol.iterator] = function () { let ...

Disabling a Drop-down Form Element in Reactive Forms with Angular 4 and above

I am currently facing an issue with disabling a select form control in my reactive form. I have searched for a solution, but haven't found a straightforward answer yet. The code I am using seems to work fine for regular input controls, but not for sel ...

Changing the value within a nested object dynamically in Angular 6 during execution

Currently working with angular 6 and have a method like this: public Save(): void { this.data.station.parkingSlot.forEach(p => { if(p.isAvailable){ p.isAvailable = false; this._carService.addCar(this.data); return true; ...

Angular checkbox filtering for tables

I have a table populated with data that I want to filter using checkboxes. Below is the HTML code for this component: <div><mat-checkbox [(ngModel)]="pending">Pending</mat-checkbox></div> <div><mat-checkbox [(ngModel ...

Using getters in a template can activate the Angular change detection cycle

When using getters inside templates, it seems that Angular's change detection can get stuck in a loop with the getter being called multiple times. Despite researching similar issues, I have not been able to find a clear solution. Background info: I ...

Transform your TypeScript code with a jscodeshift codemod that removes generic type declarations while preserving the wrapped

I am currently working on developing a codemod that will eliminate all instances of the $ReadOnly<T> generic from a TypeScript codebase, while retaining only T (where T represents an object or union). This is what I have managed to come up with so f ...

The type 'angular' does not have a property of this kind

Having trouble importing a method into my Angular component. An error keeps popping up: Property 'alerta' does not exist on type 'typeof PasswordResetService'. any I've double-checked the code and everything seems to be in order! ...

Display data in a nested array format using *ngFor in a table population

I am looking for a table that can compare two popular products. My JSON model includes an array nested within another, structured like this: products: [ { name: 'Product Basic', price: 9.90, category: 1, features: [ ' ...

What is the best way to selectively retrieve a combination of keys from multiple objects within an array?

Consider the following scenario: const common = { "test": "Test", "test2": "Test2" } const greetings = { "hello": "Hello" } export const locales = (["en", "nl"] as const); export type I18nMap = Record<typeof loc ...

Make sure to verify the optional parameter before using it in your code

Is it possible for TypeScript compiler to detect errors in code such as this, with certain tsconfig rules in place? function buildName(firstName: string, lastName?: string) { return firstName + " " + lastName; } I believe that if there is no c ...

The Typescript object property is deemed as 'undefined' even though it has not been defined

I'm relatively new to working with Typescript and I'm facing a challenge that seems quite silly: When I fetch an 'agent' object from a service. this.agentsController.getAgent(matricule).subscribe({ next: agent => { con ...

Typescript failing to verify the data within an object being extended to fulfill a type

In my coding project, I have defined an initial interface called IThing, which serves as the base for several other interfaces such as IThingA, IThingB, and more. interface IThing{ id: string; } interface IThingA extends IThing{ a1: string; a2 ...

Exploring TypeScript: Strategies for typing multi-dimensional arrays with varying shapes

How can I design an interface for a two-dimensional array that may vary in shape and contain either numbers or strings? For instance: // scenario one [[0]] // scenario two [ [0, 'I', 0, 0], [0, 'I', 0, 0], [0, 'I', 0, ...

What is the solution to resolving the websocket.onclose issue in Thirdweb?

I'm a beginner with next.js and I'm currently working on a website that utilizes web3 technology, so I need to incorporate thirdweb. My first step was running the following command to add thirdweb to my project: yarn add @thirdweb-dev/react @thi ...

Could you explain the distinction between npm install and sudo npm install?

I recently switched to using linux. To install typescript, I ran the following command: npm i typescript Although there were no errors during the installation process, when I checked the version by typing tsc --version, I encountered the error message -bas ...

Steps for generating a fresh type denotation from a value within an object

Is it possible to create a new type alias based on an object's values? const test = { 'a': ['music','bbq','shopping'], 'b': ['move','work'] }; How can we extract this information f ...

Merge the variables extracted from an array of objects

I need to extract specific data from an array of objects and perform a calculation. For example, the provided data is as follows: const item = [{ "act": "Q", "line": 1, &quo ...

Is it possible to modify the output type of a function depending on a parameter's characteristic?

In my code, I have a custom utility function that wraps document.querySelector function querySelector<T extends HTMLElement>(selector: string) { return document.querySelector<T>(selector); } I modified it to include an option to throw an e ...