Generics, reminds me to early days of studying typescript, when i see those T, U, V characters inside < brackets > that scared the hell out of me and i try to avoid them.
once i understand them, they are actually not that scary, they are powerful.
I collected examples that will help you quickly understand why, and how to use generics, without have to read through the complex documentation and confusing explanations. 😭
What are Generics?
Generics let you pass a type as a parameter to functions, interfaces, or classes.
Think of it as a function parameter, but for types.
function doSomething<T>(input: T): T {
return input;
}
Here, T is a type placeholder. When you use the function, T gets replaced by the actual type you pass.
T is just convention, you can actually name it anything you like,
function doSomething<TTypeBeingPassedHere>(input: TTypeBeingPassedHere): TTypeBeingPassedHere {
return input;
}
To give distinct identifier, commonly its starts with T followed by the name, here is another example
function doSomething<TInput>(inputOne: TInput): TInput {
// ... some logic here
return input;
}
doSomething<number>(1) // returns number
doSomething<string>("1") // returns string
This code defines the function must always return TInput, and it passed by the of the caller,
in this case TInput passed as number hence it will return number
🧐 Why Use Generics?
Avoid using any (which removes type safety)
Avoid code duplication
Write reusable and type-safe utilities
to easier understand how generic works and its benefits, take a look of more examples below.
Common use case scenarios
Reusing Logic Without Losing Types
Scenario: You want to write a utility function that works with any type but still keeps the original type intact.
function wrapInArray<T>(value: T): T[] {
return [value];
}
const numberArray = wrapInArray(123); // number[]
const stringArray = wrapInArray('hello'); // string[]
🧠 Why use generics?
- If you used any, you’d lose type safety.
- If you hardcoded string, you couldn’t reuse it for number.
Handling API Responses
Scenario: You fetch data from different endpoints. All responses follow the same format: data + success. But the data can vary.
interface ApiResponse<T> {
data: T;
success: boolean;
}
const userResponse: ApiResponse<{ id: number; name: string }> = {
data: { id: 1, name: 'Alice' },
success: true,
};
const postsResponse: ApiResponse<string[]> = {
data: ['post 1', 'post 2'],
success: true,
};
🧠 Why use generics?
- You avoid duplicating the structure. ApiResponse can be re-used for different response shape.
- You get full type safety no matter what data is.
Creating Flexible List Utilities
Scenario: You want a function like getFirstItem, and you want it to work for any array type.
function getFirstItem<T>(list: T[]): T | undefined {
return list[0];
}
const firstName = getFirstItem(['Rick', 'Morty']); // string
const firstNumber = getFirstItem([10, 20, 30]); // number
🧠 Why use generics?
Extending Built-in Types Safely
Scenario: You want to write a function that only accepts objects that have a length, like strings or arrays — not numbers or booleans.
function logLength<T extends { length: number }>(value: T): void {
console.log(value.length);
}
logLength('hello'); // valid
logLength([1, 2, 3]); // valid
logLength(100); // ❌ Error: number doesn't have length
🧠 Why use generics with extends?
- Combined with use of Extends, you can selectively accepts types of Generics you allow, in this case logLength only accepts anything that compatible with
{ length: number} - You define a flexible type that still enforces structure.
React Hook That Works with Any Type
Scenario: You want a useLocalStorage hook that works with any type of value — string, number, object, etc.
function useLocalStorage<T>(key: string, initialValue: T): [T, (val: T) => void] {
const [value, setValue] = React.useState<T>(() => {
const json = localStorage.getItem(key);
return json ? JSON.parse(json) : initialValue;
});
React.useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
🧠 Why use generics?
- So this hook works with boolean, string, { id: number }, or anything else, with full autocomplete and safety.
Generic Class for Storing Any Type
Scenario: You want a class that stores data of any type.
class Store<T> {
private items: T[] = [];
add(item: T) {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
}
const userStore = new Store<{ id: number; name: string }>();
userStore.add({ id: 1, name: 'Alice' });
const numberStore = new Store<number>();
numberStore.add(10);
🧠 Why use generics?
- One class works for both objects, strings, and numbers - without rewriting the logic.
official reference https://www.typescriptlang.org/docs/handbook/2/generics.html
Top comments (0)