Learn how to create flexible, reusable types with TypeScript generics
Updated:
TypeScript UniversityTypeScriptGenerics
The concept of composition, or the idea that “I want to use this same piece of
logic in multiple places,” is the foundation of software design and
development. This usually manifests as methods or functions that rely on an
input and can perform logic or generate an expected output.
You can think of generic types as a special type that can accept a type as an
input and return an output based on the input type. An example would be a shared
APIResult type.
These declarations work great for these simple examples, but they can be a
burden to maintain. For example, if you look closely the next_page property
for each return type is number | null. This is a typo. Each next_page
should be a URL of type string | null. Now imagine having to maintain more
than two tables.
For this reason, we want to define what a successful API response is shaped
like.
typeAPISuccessResult<T> = {
data:T;
page:number;
next_page:string|null;
status:200;
};
The T is the input type. So if a consumer passes in a string, then
APISuccessResult["data"] is of type string.
type
typeProduct= {
id:string;
price:number;
name:string;
categories:string[];
}
Product= {
id: string
id:string;
price: number
price:number;
name: string
name:string;
categories: string[]
categories:string[];
};
type
typeAPISuccessResult<T> = {
data:T;
page:number;
next_page:string|null;
status:200;
}
APISuccessResult<
function (typeparameter) TintypeAPISuccessResult<T>
T> = {
data: T
data:
function (typeparameter) TintypeAPISuccessResult<T>
T;
page: number
page:number;
next_page: string |null
next_page:string|null;
status: 200
status:200;
};
type
typeProductSearchApiResult= {
data:Product;
page:number;
next_page:string|null;
status:200;
}
ProductSearchApiResult=
typeAPISuccessResult<T> = {
data:T;
page:number;
next_page:string|null;
status:200;
}
APISuccessResult<
typeProduct= {
id:string;
price:number;
name:string;
categories:string[];
}
Product>;
We can also constrain the input type by using the extends keyword.
type
typeAPIErrorResult<Textends400|500> = {
error: {
message:string;
};
status:T;
}
APIErrorResult<
function (typeparameter) TintypeAPIErrorResult<Textends400|500>
Textends400|500> = {
error: {
message: string;
}
error: {
message: string
message:string;
};
status: T extends 400|500
status:
function (typeparameter) TintypeAPIErrorResult<Textends400|500>
T;
};
type
typeValidProductSearchApiResult= {
error: {
message:string;
};
status:400;
}
ValidProductSearchApiResult=
typeAPIErrorResult<Textends400|500> = {
error: {
message:string;
};
status:T;
}
APIErrorResult<400>;
type
typeInvalidProductSearchApiResult= {
error: {
message:string;
};
status:200;
}
InvalidProductSearchApiResult=
typeAPIErrorResult<Textends400|500> = {
error: {
message:string;
};
status:T;
}
APIErrorResult<200>;
Error ts(2344) ― Type '200' does not satisfy the constraint '400 | 500'.