I want to constraint the calling party from entering random strings as parameters to a method:
// A class providing strings (urls)
class BackendUrls {
static USERS_ID = (id: string) => `/users/${id}`;
static CONSTANTS = () => '/constants';
}
// Base class with 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
}
}
What I want to achieve is constraining the parameter to BackendService->GET method.
How can I prevent UserService.loadUser(..) from entering a random string and force it to use one of BackendUrls static members instead?
What I've tried 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> { // <-- ??
// ...
}
}
But that still does not prevent the calling code from entering a simple string:
return this.GET<User>(`/users/${id}`);
Edit: Please not that the calling party should be forced to use the BackendUrls class' static members (which are all functions).
CodePudding user response:
You can get part way to what you wish to achieve with const assertions, e.g.
const urls = ['/constants','/users'] as const
type Url = typeof urls[number]
// Base class with method in question (GET)
class BackendService {
protected GET<T>(url: Url): Observable<T> {
// ...
}
}
I don't think you're going to be able to write code that validates types against an infinite number of patterns unless you write some validation logic in the method however.
CodePudding user response:
Solved it by using an interface instead of type:
export interface BackendUrl {
value: string;
}
class BackendUrls {
static USERS_ID = (id: string): BackendUrl => ({ value:`/users/${id}` });
static CONSTANTS = () : BackendUrl => ({ value:'/constants' });
}
.. and then used the 'value' inside the GET method.