Creating a TypeScript generic that can traverse the routes configuration tree while accumulating the path is a useful skill to have

I am struggling with defining a TypeScript generic to achieve a specific output.

type Route = {
  path: string
  children?: readonly Route[]
}

const root = {
  path: "a",
  children: [
    { path: "b" },
    { path: "c", children: [{ path: "d", children: [{ path: "e" }] }] }
  ],
} as const satisfies Route;

The goal is to create a TypeScript generic Convert that will result in the enum 'a' | 'a/b' | 'a/c' | 'a/c/d' | 'a/c/d/e' when used with Convert<typeof root>.

type Convert<T extends Route> = ?

My attempt so far has not been successful:

type Convert<T extends Route, Prefix extends string = ''> = 
`${Prefix}/${T['path']}` | (
    T["children"] extends readonly Route[]
      ? {
        [K in number]: Convert<T['children'][K], `${Prefix}/${T['path']}`>;
      }[number]
      : never)

The current output I get is limited to "/a", "/a/b", and "/a/c", and doesn't go deep enough.

Click here to view and help me on TypeScript Playground

Any assistance would be greatly appreciated!

Answer №1

Exploring various approaches is key in this scenario. Here's a particular method:

type Convert<T extends Route> =
  T['path'] | (
    T extends { children: readonly (infer R extends Route)[] } ?
    `${T['path']}/${Convert<R>}` : never
  );

This code snippet simply outputs the path property and recursively extracts R, which represents the union of Routes within the children property while applying Convert<R>. This result is then appended to the existing path property with a slash.

To verify its functionality:

type Expect = 'a' | 'a/b' | 'a/b/c' | 'a/b/c/d' | 'a/b/c/d/e'
type Actual = Convert<typeof root>;
// type Actual = "a" | "a/b" | "a/c" | "a/c/d" | "a/c/d/e"

Everything seems to be in order.

Link to play around with the code in 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

unanticipated installation issue with published npm package on verdaccio

I am on a mission to create and release a package containing commonly used TypeScript functions on Verdaccio, an npm registry that operates on Docker. After completing the build for my TypeScript package, this is how my project structure looks: https://i ...

Instead of leaving an Enum value as undefined, using a NONE value provides a more explicit

I've noticed this pattern in code a few times and it's got me thinking. When you check for undefined in a typescript enum, it can lead to unexpected behavior like the example below. enum DoSomething { VALUE1, VALUE2, VALUE3, } f ...

Alternative for using useRouteMatch to retrieve parameters

I'm currently refactoring this code and struggling to find a suitable replacement for this section. This is due to the react-router-dom v6 no longer having the useRouteMatch feature, which I relied on to extract parameters from the URL: import React, ...

Establishing the parameters for a list that is not empty using a specific data type

Is it feasible to establish a non-empty list within TypeScript's type system? While I am aware that it is possible to define a list with a specific number of elements, similar to a Tuple: type TwoElementList = [number, number]; This approach is limi ...

File declaring Vue3 Typescript types

I'm working with a Vue3 plugin in JavaScript, here's a snippet of the code: const myPlugin = { install(Vue, config) { // do something } export default myPlugin; This code is located in index.js and will be called by app.use. F ...

Combine values from 2 distinct arrays nested within an array of objects and present them as a unified array

In my current array, the structure looks like this: const books[{ name: 'book1', id: '1', fromStatus: [{ name: 'Available', id: 1 }, { name: 'Free', id: 2 }], t ...

Using Typescript Type Guard will not modify the variable type if it is set in an indirect manner

TL;DR Differentiation between link1 (Operational) vs link2 (not functional) TypeGuard function validateAllProperties<T>(obj: any, props: (keyof T)[]): obj is T { return props.every((prop) => obj.hasOwnProperty(prop)) } Consider a variable ms ...

Alter the attributes of an instance in a class using a function

Attempting to explain a simple method in TypeScript. This method should allow modification of data for any object type within the data attribute. In simpler terms, we can modify, add, or remove data based on the specified data type, and TypeScript facilit ...

Incorporate Canvg version 4 into a TypeScript project

I am currently facing an issue with an older TypeScript project that has the following tsconfig setup: { "compilerOptions": { "baseUrl": "./src", "outDir": "build/dist", "module": &q ...

What is the process of changing a number to the double data type in JavaScript or TypeScript?

Within a single input field, users can enter various numbers such as 12, 12.1, 12.30, and 12.34. The challenge is to pass this value in a service call where only the value can be sent as a number but with two decimal points. let a = input //a will be a ty ...

Is it possible in Typescript to restrict object creation to Record<string, T> while still being able to access the defined

Suppose I define a type called Param as follows: type Param = { field: string; active: boolean; } I then use this type to create a record with keys of type string and values of type Param, like so: const params: Record<string, Param> = { foo: { f ...

The attribute 'finally' is not found on the data type 'Promise<void>'

I've been attempting to implement the finally method on a promise but continue running into this issue. Property 'finally' does not exist on type 'Promise<void>'. After researching similar problems, I found suggestions to a ...

What could be causing NgModel to fail with mat-checkbox and radio buttons in Angular?

I am working with an array of booleans representing week days to determine which day is selected: selectedWeekDays: boolean[] = [true,true,true,true,true,true]; In my HTML file: <section> <h4>Choose your days:</h4> <mat-che ...

In Typescript, it is not permitted to assign a variable as a value within a styled array

Encountering a peculiar issue with TypeScript, Emotion.css, and React. The following code functions without any issues: import styled from '@emotion/styled-base'; const Layout = styled('div')([ { height: 48, color: ...

There is an issue with the Svelte TypeScript error related to the $: syntax, specifically stating that a declaration cannot be used in a

I encountered an issue where I am receiving the error message "Cannot use a declaration in a single-statement context" while using $: syntax in a script with lang="ts" within my +page.svelte file. Additionally, when I check the version control system (VCS) ...

The Angular Reactive Forms error message indicates that attempting to assign a 'string' type to an 'AbstractControl' parameter is invalid

While attempting to add a string value to a formArray using material forms, I encountered the following error message: 'Argument of type 'string' is not assignable to parameter of type 'AbstractControl'.' If I try adding a ...

How can I activate TypeScript interface IntelliSense for React projects in VSCode?

Did you know that there are TypeScript interfaces designed for DOM, ES5, and other JavaScript modules? I am curious if it is feasible to have intellisense similar to the one provided by TypeScript Playground for various interfaces in a React project. ...

Navigating through different components in Angular 4 using a service for routing

Encountering an issue while connecting my Angular 4 and REST application with a service. Here's the error message: compiler.es5.js:1694 Uncaught Error: Can't resolve all parameters for TypeaheadComponent: (?, [object Object], [object Object]). ...

Tips on personalizing the FirebaseUI- Web theme

Can someone help me find a way to customize the logo and colors in this code snippet? I've only come across solutions for Android so far. if (process.browser) { const firebaseui = require('firebaseui') console.log(firebaseui) ...

How can I assign Angular 2's HTTP object to a variable?

Here is the code I am currently using: private ARRAYDATA: any[]; constructor(private http: Http) { this.getCards('data.json'); } getCards(url) { this.http.get(url) .map(response => response.json()) .subscr ...