Restricting Method Arguments in TypeScript to a Specific Type

I am looking to restrict the calling party from inputting arbitrary strings as parameters to a method:

// A class that provides string values (urls)
class BackendUrls {
 
    static USERS_ID     = (id: string)  => `/users/${id}`;
    static CONSTANTS    = ()            => '/constants';
    
}
    
// Base class containing the method in question (GET)
class BackendService {
    
    protected GET<T>(url: string): Observable<T> {
        // ...
    }
   
}
    
// Calling party (subclass)
class UserService extends BackendService {
    
    loadUser(id: string): Observable<User> {
        return this.GET<User>(BackendUrls.USERS_ID(id));
        // return this.GET<User>(`/users/${id}`); // <-- this also works
    }
   
}

My goal is to constrain the parameter used in the BackendService->GET method.

How can I prevent UserService.loadUser(..) from using an arbitrary string and enforce usage of one of BackendUrls static members instead?

What I have attempted so far:

type BackendUrl = string;

class BackendUrls {

    static USERS_ID     = (id: string): BackendUrl  => `/users/${id}`;
    static CONSTANTS    = ()          : BackendUrl  => '/constants';

}

class BackendService {

    protected GET<T>(url: BackendUrl): Observable<T> { // <-- ??
        // ...
    }

}

However, this approach still does not prevent the caller from providing a simple string:

return this.GET<User>(`/users/${id}`);

Edit: Note that the calling party should be mandated to use the functions provided by the BackendUrls class' static members.

Answer №1

If you want to approach your desired outcome using const assertions, you can take some steps in that direction, such as:


const urls = ['/constants','/users'] as const

type Url = typeof urls[number]
// Defining a base class with the relevant method (GET)

class BackendService {
    
    protected GET<T>(url: Url): Observable<T> {
        // Implementation logic goes here
    }
}

It seems unlikely that you will be able to create code that can validate types for an endless array of patterns without incorporating some validation logic within the method itself.

sandbox

Answer №2

Found a solution by implementing an interface instead of type:

export interface BackendUrl {

    link: string;

}

class BackendUrls {

    static USERS_ID     = (id: string): BackendUrl  => ({ link:`/users/${id}` });
    static CONSTANTS    = ()          : BackendUrl  => ({ link:'/constants' });

}

.. and then utilized the 'link' property in the GET method.

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

What is preventing me from using property null checking to narrow down types?

Why does TypeScript give an error when using property checking to narrow the type like this? function test2(value:{a:number}|{b:number}){ // `.a` underlined with: "Property a does not exist on type {b:number}" if(value.a != null){ ...

Tips for maintaining knowledge after redirecting to a new page

Building an app using Ionic 4 where I need to display vouchers from a database as images. Each image should act as a link to a details page showing more information about that specific voucher. However, I am struggling to figure out how to keep track of th ...

Struggling with continuously re-rendering a color background when using useMemo in React?

After every re-render, a new color is generated. Is there a way to store the initial color and reuse it in subsequent renders? const initialColor = generateNewColor(); // some random color const backgroundColor = React.useMemo(() => { return ...

Sending error messages from server to client (leveraging Express and Backbone)

I'm struggling with passing server error messages to a client after thrashing around for a while. Here's what I have on the server side (simplified): export function get(req: express.ExpressServerRequest, res: express.ExpressServerResponse) { ...

getItemForm does not make a second promise call

I have a scenario where my function calls the api.send service twice, however when I run a test expecting it to resolve both promises, only res1 is returned and not res2. How can I ensure that both promises are resolved successfully? Here is my function: ...

Components unable to exchange data using Service

Trying to pass values from one component to another, but encountering issues with the code. The values are showing as undefined on the next page. Below is the code for my service class named UtilityService.ts: import { Injectable } from '@angular/co ...

ngx-slick-carousel: a carousel that loops infinitely and adjusts responsively

I have implemented ngx-slick-carousel to showcase YouTube videos in my project. However, I am facing two main issues. Firstly, the carousel is not infinite, and when the last video is reached, there appears to be white spaces before it loops back to the fi ...

Importing multiple modules in Typescript is a common practice

I need to include the 'express' module in my app. According to Mozilla's documentation, we should use the following code: import { Application }, * as Express from 'express' However, when using it in TypeScript and VSCode, I enc ...

Passing a service into a promise in Angular 2 using TypeScript

Is there a way to pass a service into a promise? I am currently working on a promise that will only resolve once all the http requests are complete. However, I am facing an issue where this.jiraService is undefined. Is there a method to pass it to the co ...

Tips for quietly printing a PDF document in reactjs?

const pdfURL = "anotherurl.com/document.pdf"; const handleDirectPrint = (e: React.FormEvent) => { e.preventDefault(); const newWin: Window | null = window.open(pdfURL); if (newWin) { newWin.onload = () => ...

Developing a customizable Angular application for a diverse range of users

In my current project, I am developing an Angular front-end application that showcases a consistent layout and interface, but with the ability to customize company-specific elements such as logos, colors, and other variables. The main objective is to creat ...

Angular service is able to return an Observable after using the .then method

I am currently facing an issue with retrieving the authentication status in a service method. Everything seems to be working fine except for the return statement. I am struggling with the usage of .then inside .map and I am unable to figure out how to retu ...

Error: Reference to an undeclared variable cannot be accessed. TypeScript, Cordova, iOS platforms

Can anyone offer some advice on what might be the issue? I'm encountering an error while building my Ionic app on the IOS platform, but everything is running smoothly on Android. ReferenceError: Cannot access uninitialized variable. service.ts:31 O ...

Learn about Angular8's prototype inheritance when working with the Date object

In my search for a way to extend the Date prototype in Angular (Typescript), I stumbled upon a solution on GitHub that has proven to be effective. date.extensions.ts // DATE EXTENSIONS // ================ declare global { interface Date { addDa ...

What is the best way to mimic a library using SinonJs?

I am dealing with a file named browser-launcher.ts import * as Browser from "@lib/browser"; class BrowserLauncher { launch(options) { browser = Browser(options); } } export const browserLauncher = new BrowserLauncher() W ...

Implement a for loop within the function responsible for creating a collection in Firebase

I am currently developing a food application using Ionic (4) /Angular that can manage multiple stores and integrates Firebase. However, I have encountered a problem. When creating a new order, I use the following code: add(stores: Array<CartStore>, ...

Encountering a problem with the 'string' parameter when using TypeScript

I keep encountering the following error message: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ barkingRoadProject: string[]; }'. No index signature with a paramet ...

What could be causing FormArrayName to consistently display as undefined in my HTML code, even when the safe-navigation operator is employed

Currently, I'm referring to the Angular Material example found at https://angular.io/api/forms/FormArrayName Upon initializing the packageForm formGroup in ngOnInit() and logging it in the console during ngAfterViewInit(), I can see that the translat ...

Ways to resolve the issue of the 'setConfirmDelete' property not being found on type 'JSX.IntrinsicElements' in React.js

index.tsx const setConfirmDelete = (state, close) => { return ( <Modal show={state} onHide={close}> <Modal.Header> <Modal.Title>Title</Modal.Title> </Modal.Header> <Modal.Body> 'T ...

Can TypeScript Implement a Dictionary Feature Similar to C#?

Looking for guidance on how to use TypeScript interface to define these C# models: public class PageModel { public long Id { get; set; } public string Name { get; set; } public IDictionary<string, FieldModel> Fields { get; set; } } pu ...