TypeScript generics is a topic I needed help grasping for a long time. I decided to learn about it and write a blog post to understand it better. Besides understanding generics, we will see their usage with a real-world example involving a React custom useFetch
hook.
useFetch
hook example showcased how generics can help you handle diverse API responses while maintaining type correctness.Generics in TypeScript provide a way to write functions, classes, and interfaces that work with various data types while maintaining type information. This allows you to create more flexible and reusable components that support type safety. Generics are indicated by angle brackets <T>
following a function or type declaration.
Let's start with a basic example using a generic function:
1// Generic function to print elements of an array2function printArrayElements<T>(arr: T[]): void {3 arr.forEach((item) => console.log(item));4}5
6const stringArray: string[] = ['apple', 'banana', 'cherry'];7const numberArray: number[] = [1, 2, 3];8
9printArrayElements(stringArray);10printArrayElements(numberArray);
Here, the printArrayElements
function can handle both arrays of strings and numbers, ensuring type safety.
Let's dive into a practical scenario where generics shine: creating a dynamic type for a React useFetch
hook. This hook fetches data from an API and provides the fetched data, error information, and loading state to the component. Generics will help us handle different response types from various endpoints.
1import { useState, useEffect } from 'react';2
3// Define a generic type for the response data4interface ApiResponse<T> {5 data: T | null;6 error: Error | null;7 loading: boolean;8}9
10// Define a custom hook that fetches data11function useFetch<T>(url: string): ApiResponse<T> {12 const [data, setData] = useState<T | null>(null);13 const [error, setError] = useState<Error | null>(null);14 const [loading, setLoading] = useState(true);15
16 useEffect(() => {17 async function fetchData() {18 try {19 const response = await fetch(url);20 const responseData = await response.json();21 setData(responseData);22 } catch (err) {23 setError(err);24 } finally {25 setLoading(false);26 }27 }28
29 fetchData();30 }, [url]);31
32 return { data, error, loading };33}34
35// Usage in a component36function App() {37 const apiUrl = 'https://api.example.com/data'; // Replace with your API URL38
39 const { data, error, loading } = useFetch<{ message: string }>(apiUrl);40
41 if (loading) {42 return <p>Loading...</p>;43 }44
45 if (error) {46 return <p>Error: {error.message}</p>;47 }48
49 return (50 <div>51 <h1>API Data</h1>52 <p>{data?.message}</p>53 </div>54 );55}56
57export default App;
Generics offer a versatile solution for various scenarios:
Sign up to get updates when I write something new. No spam ever.
Subscribe to my Newsletter