If You want to write generic class for server requests this post is for You.
I preffer code examples over the words and funny images, so You will not spend a lot of time here.
Let's define our allowed endpoints and constraints:
const enum Endpoints {
users = '/api/users', // GET | POST
notes = '/api/notes', // POST | DELETE
entitlements = '/api/entitlements' // GET
}
Let's assume that backend developer allowed You to make:
-
GET
|POST
requests forusers
-
POST
|DELETE
requests fornotes
-
GET
requests forentitlements
Now, we can also define allowed methods for each endpoint :
interface HandleUsers {
get<T>(url: Endpoints.users): Promise<T>;
post(url: Endpoints.users): Promise<Response>;
}
interface HandleNotes {
post(url: Endpoints.notes): Promise<Response>;
delete(url: Endpoints.notes): Promise<Response>;
}
interface HandleEntitlements {
get<T>(url: Endpoints.entitlements): Promise<T>;
}
Now, we can define our main class:
class Api {
get = <T = void>(url: Endpoints): Promise<T> => fetch(url)
post = (url: Endpoints) => fetch(url, { method: 'POST' })
delete = (url: Endpoints) => fetch(url, { method: 'DELETE' })
}
For now, class Api
does not have any constraints.
So let's define them:
// Just helper
type RequiredGeneric<T> = T extends void
? { __TYPE__ERROR__: 'Please provide generic parameter' }
: T
interface HandleHttp {
<T extends void>(): RequiredGeneric<T>
<T extends Endpoints.users>(): HandleUsers;
<T extends Endpoints.notes>(): HandleNotes;
<T extends Endpoints.entitlements>(): HandleEntitlements;
}
As You see, HandleHttp
is just overloading for function. Nothing special except the first line. I will come back to it later.
We have class Api
and overloadings for function. How we can combine them? Very simple - we will just create a function which returns instance of Api class
.
const handleHttp: HandleHttp = <_ extends Endpoints>() => new Api();
Take a look on generic parameter of httpHandler
and HandleHttp
interface, there is a relation between them.
Let's test our result:
const request1 = handleHttp<Endpoints.notes>() // only delete and post methods are allowed
const request2 = handleHttp<Endpoints.users>() // only get and post methods are allowed
const request3 = handleHttp<Endpoints.entitlements>() // only get method is allowed
Wait, what if I forgot to set generic parameter for handleHttp
?
Trust me, this is not a problem :) Just hover the mouse on request
. Now You, why I used RequiredGeneric
const request = handleHttp() // 'Please provide generic parameter'
You will not able to call any method without generic parameter.
P.S. I believe I used here typestate pattern.
It is the end folks)
Top comments (0)