When developers discuss Typescript, one of their main complaints is that you have to write more code to achieve the same result. While I agree with that, I think Typescript Generics can reduce the code you need to write to a great extent.
What are TypeScript Generics?
TS Generics can be used to bring abstraction into your TS interfaces. Using generics, you can pass an interface as a param to another interface.
Here is an example of a standard API response for both a happy path and an error case.
// successful response ✅
{
status: 'ok',
responseCode: 200,
data: {...}
}
// error response ❌
{
responseCode: 500,
errorMessage: "Something went wrong ?";
}
Instead of writing an interface for every response and adding these keys, you can simply use Generics to create something like this:
interface ApiResponse<T>{
errorMessage?: string;
responseCode?: string;
data?: T;
}
interface UserData {
name: string;
email: string;
}
const response: ApiResponse<UserData> = {}
Linking Generics with functions
Let's assume we have a function that we use to make an API request to our backend.
const getRequestTo = (endpoint: string) => {
return fetch(process.env.BE_HOST + endpoint).then(res => res.json())
}
const userResponse = getRequestTo('/user-data')
The type of userResponse
would be any
. We can have a better TypeScript implementation here.
const getRequestTo = async <R>(endpoint: string): Promise<ApiResponse<R>> => {
const request = await fetch(process.env.BE_HOST + endpoint);
const response = await request.json();
return response;
};
We can create a similar function for POST requests which also takes JSON as params of type B
and the server will send back a JSON response of type R
:
const postRequestTo = async <B, R>(
endpoint: string,
body: B
): Promise<ApiResponse<R>> => {
const request = await fetch(process.env.BE_HOST + endpoint, {
method: "POST",
body: JSON.stringify(body),
});
const response = await request.json();
return response;
};
Similarly, there can be a PATCH request function as well, which handles partial updates of any entity.
const patchRequestTo = async <B, R>(endpoint: string,body: Partial<B>): Promise<ApiResponse> => {
const request = await fetch(process.env.BE_HOST + endpoint, {
method: "PATCH",
body: JSON.stringify(body)
});
const response = await request.json();
return response;
};
Here is how to implement something like that:
interface RequestBody {}
interface Response {}
const createPost = await postRequestTo<RequestBody, Response>('/post', postData);
const updatePost = await patchRequestTo<RequestBody, Response>('/post', {
title: "new name"
});
Let's pull it all together with a simple JavaScript class:
class ApiService<T> {
constructor(entitySlug: string) {
this.entitySlug = entitySlug;
}
private entitySlug: string;
getOne = async (id: string): Promise<APIResponse<T>> => {
const request = await fetch(process.env.BE_HOST + this.entitySlug + '/' + id);
const response = await request.json();
return response;
};
getList = async (): Promise<APIResponse<T[]>> => {
const request = await fetch(process.env.BE_HOST + this.entitySlug);
const response = await request.json();
return response;
};
create = async (body: Omit<T, 'id' | 'created' | 'updated'>): Promise<APIResponse<T>> => {
const request = await fetch(process.env.BE_HOST + this.entitySlug, {
method: 'POST',
body: JSON.stringify(body)
});
const response = await request.json();
return response;
};
update = async (
body: Omit<Partial<T>, 'id' | 'created' | 'updated'>
): Promise<APIResponse<T>> => {
const request = await fetch(process.env.BE_HOST + this.entitySlug + '/' + body.id, {
method: 'PATCH',
body: JSON.stringify(body)
});
const response = await request.json();
return response;
};
delete = async (id: string): Promise<void> => {
const request = await fetch(process.env.BE_HOST + this.entitySlug + '/' + id, {
method: 'DELETE'
});
const response = await request.json();
return response;
};
};
and you can then create an entity service like this:
export const postService = new ApiService<Post>('/post');
and it's all linked and ready for you.
![image-70](https://www.freecodecamp.org/news/content/images/2020/07/image-70.png)
Typescript has the potential to increase the developer experience tenfold if appropriately configured. These are some strategies to make that configuration process more comfortable. I hope this will help you use Typescript better in your current codebase.