Typescript now supports recursive template literal types, enabling a combination of string and specific chaining

Minimal, Complete, and Verifiable Example (MCVE)

Check out the live demo here

(try using the autocomplete on the parameter of the function f to see what it achieves)

I am currently working on creating a recursive template literal to validate if the strings passed to a function align with the REST approach of my API.

So far, I have been successful in making simple examples work, such as version and health/check.

However, I am looking to extend this functionality to allow routes with parameters embedded in them.

While the route users/[PARAM] is accepted without any issues and shows up in the autocomplete for parameters, users/12 does not pass validation. The problem seems to lie at the end of the Dive type:

`${k & DiveKey}/${typeof param | string}`

If I utilize

${k & DiveKey}/${typeof param}
, I achieve the expected outcome.

Using ${k & DiveKey}/${string} allows it to work, but the parameter users/[PARAM] no longer appears in the autocomplete, making it less visible to developers utilizing the function.

Therefore, my question boils down to: Is there a way to retain users/[PARAM] in the autocomplete while also accepting users/12 as an input?

Thank you in advance for your assistance.

Code Snippet

export type RestEndpoint = Dive;

const param = '[PARAM]' as const;
type DiveKey = string | number;

type Dive<T = typeof API_REST_STRUCTURE> = keyof {
    [k in keyof T as T[k] extends Record<any, any>
        ? `${k & DiveKey}/${Dive<T[k]> & DiveKey}`
        : T[k] extends typeof param
        ? `${k & DiveKey}/${typeof param | string}`
        : k]: any;
};

const API_REST_STRUCTURE = {
  version: false,
  health: { check: false },
    users: param,
};

function f(p: RestEndpoint) {}

f('version');
f('health/check');
f('users/[PARAM]');
f('users/12');

Update 1: Here's how it looks when using ${string}:

https://i.sstatic.net/vIjBu.png

And this is the result with typeof param (notice that users/12 is rejected):

https://i.sstatic.net/ibBry.png

Answer №1

At the moment, "pattern" template literal types with placeholders like template literal types, as introduced in microsoft/TypeScript#40598, are not displayed in auto-suggest or auto-complete lists in TypeScript. This functionality is missing and has been requested in microsoft/TypeScript#41620. For now, a workaround needs to be implemented if you want suggestions related to such types.


One workaround involves creating two types: one containing "[PARAM]" and another with string. Then, make your f() function generic in a manner where the "[PARAM]" version will be suggested, but any string value will be accepted as well.

... (content continues) ...

Link to Playground for code

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

Expanding external type declarations within Typescript

I am currently working with Typescript and the ant design library. My goal is to extend an existing interface by adding a single property. To start, I imported the original interface so you can see the folder structure: import { CollapseProps } from &apo ...

What is the best way to create a personalized filter function for dates in JavaScript?

I am working with a DataTable that includes a column called Timestamp: <p-dataTable sortMode="multiple" scrollable="scrollable" scrollHeight="150" [value]="currentChartData" #dt> <p-column field="timestamp" header="Timestamp" [sortable]=" ...

Make sure to wait for the observable to provide a value before proceeding with any operations involving the variable

Issue with handling observables: someObservable$.subscribe(response => this.ref = response); if (this.ref) { // do something with this.ref value } ERROR: this.ref is undefined How can I ensure that the code relying on this.ref only runs after ...

Build upon a class found in an AngularJS 2 library

Whenever I attempt to inherit from a class that is part of a library built on https://github.com/jhades/angular2-library-example For example, the class in the library: export class Stuff { foo: string = "BAR"; } And the class in my application: exp ...

Is there a way to make a peer dependency optional in a project?

In the process of developing module A, I have implemented a feature where users can choose to inject a Winston logger into my module. As a result, winston becomes a peer dependency. However, when attempting to include module A in another module without th ...

I am curious about how this code links the Send button to the function. Additionally, I would like to know the process for creating

Having some trouble with my SPFx study project. Specifically, I'm confused about how the Send button is connected to the setComment() function in this context. I've also added an Update button that I want to use for updating the item created earl ...

What is the best way to handle typing arguments with different object types in TypeScript?

Currently, I have a function that generates input fields dynamically based on data received from the backend. To ensure proper typing, I've defined interfaces for each type of input field: interface TPField { // CRM id as a hash. id: string nam ...

Determine the data type based on a related data type

My goal is to develop a versatile function with a generic string union parameter that defines an object parameter where the property name depends on the generic parameter itself. Consider this scenario: The variable params can be either {customerId: &apos ...

Please ensure that the table contains all the records corresponding to the respective days

I am struggling with figuring out how to display a record of classes in my table view. The UX prototype I need to follow is shown https://i.stack.imgur.com/CISYn.png (the days of the week are in Portuguese: horario = time, segunda = Monday, terça = Tuesda ...

Click on the button to generate a PDF report using Internet Explorer 11

After encountering some challenges with printing a PDF report specifically on IE 11, I am reaching out for help. The code snippet below works perfectly in Chrome, but when it comes to IE 11, everything falls apart. Just to provide some context, I am develo ...

When working with Angular Universal, using d3.select may result in a "reference error: document is not defined" in the server.js file

I'm currently working on an Angular project that uses server-side rendering to generate D3 charts. Each chart is encapsulated within its own component, such as a line-chart component which consists of TypeScript, spec.ts, HTML, and CSS files for rende ...

I have attempted numerous methods, but the TypeScript object remains potentially undefined

My current code involves using canvas to capture the cropped image. Below is the function that handles this process: export const getCroppedImg = ( image: HTMLImageElement, crop: Crop, fileName: string ): Promise<Blob> => { let canvas: HTM ...

Issue with ngx-bootstrap custom typeahead feature malfunctioning

I'm facing an issue while trying to develop a customized typeahead feature that is supposed to search my API every time the user inputs something, but it's not functioning as expected. The autocomplete() function isn't even getting accessed. ...

Navigating through multiple pages using an Observable in Angular

After countless attempts, I still haven't been able to figure it out. Any assistance would be greatly appreciated; I recently came across Angular and RxJs. The issue I'm facing involves a service that fetches resources from various URLs of the s ...

Retrieving the property of a union type comprising a void type and an unnamed type

Currently, I am working on a project involving GraphQL. In my code, I have encountered a GraphQLError object with a property named extensions. The type of this property is either void or { [key: string]: any; }. Whenever I try to access any property within ...

fp-ts/typescript steer clear of chaining multiple pipes

Is there a more concise way to handle nested pipes in fp-ts when working with TypeScript? Perhaps using some form of syntactic sugar like Do notation? I'm trying to avoid this kind of nested pipe structure: pipe( userId, O.fold( () => set ...

Unable to break down the property 'desks' of '(0 , _react.useContext)(...)' due to its undefined nature

Trying to mock DeskContext to include desks and checkIfUserPresent when calling useContext is causing an error to occur: Cannot destructure property 'desks' of '(0 , _react.useContext)(...)' as it is undefined TypeError: Cannot destruct ...

Continually receiving the error message: "tsc.exe" has exited with an error code of 1

After I include a tsconfig.json file in my Visual Studio 2015 web solution, I encounter the error mentioned above. Furthermore, this prevents the compiler from generating js files again even when the "compileOnSave": true option is set. When I click on t ...

Creating a signature for a function that can accept multiple parameter types in TypeScript

I am facing a dilemma with the following code snippet: const func1 = (state: Interface1){ //some code } const func2 = (state: Interface2){ //some other code } const func3: (state: Interface1|Interface2){ //some other code } However, ...

Guide to displaying an input box depending on the selection made in a Mat-Select component

I am working on a mat-select component that offers two options: Individual Customers and Organizational Customers. When selecting Individual Customers, the dropdown should display three options: First Name, Last Name, and Customer Id. However, when choosin ...