TypeScript generics are a powerful feature that allows you to create reusable and flexible components, functions, and classes by defining them with a type variable.
Understanding Generics ๐คโ
This type variable, often denoted as <T>
, can be used in place of specific
types, enabling you to write code that can work with multiple types without
sacrificing type safety.
Generics promote code reusability and maintainability, as you can write a single, generalized function, class, or interface that can be used with different types, rather than creating separate implementations for each specific type.
Time To Transition From JavaScript To TypeScript
Level Up Your TypeScript And Object Oriented Programming Skills. The only complete TypeScript course on the marketplace you building TypeScript apps like a PRO.
SEE COURSE DETAILSBasic Synxtax ๐โ
The simplest form of TypeScript generics syntax is the type variable, usually
represented by a single letter like <T>
.
Here's a minimal example of a generic function:
function identity<T>(arg: T): T {
return arg;
}
In this example:
<T>
is the type variable. It acts as a placeholder for the actual type that will be provided when the function is called.function identity<T>(arg: T): T
is the generic function declaration. The type variable<T>
is placed right after the function name and before the parameter list.arg: T
specifies that the input parameterarg
is of typeT
. It means that the type ofarg
will be the same as the type provided when calling the function.: T
after the parameter list indicates that the return type of the function is also of typeT
. The function will return a value of the same type as the input parameter.
Now, when you call the identity
function, you can either explicitly provide
the type, like identity<number>(42)
, or let TypeScript infer it based on the
provided argument, like identity(42)
. In both cases, the function will return
a value of the same type as the input, and TypeScript will ensure type safety.
Generics With Classesโ
Generics can also be used with classes, interfaces, and type aliases. Here are examples for each of these constructs using generic types:
class Box<T> {
private content: T;
constructor(content: T) {
this.content = content;
}
getContent(): T {
return this.content;
}
}
In this example, the Box
class has a generic type <T>
. It is used as the
type of the content
property and the type of the parameter in the constructor.
The getContent()
method also returns a value of type T
. This makes the Box
class generic and able to store and return content of any type.
Generics With Interface:โ
interface KeyValuePair<K, V> {
key: K;
value: V;
}
In this example, the KeyValuePair
interface has two generic types: <K>
and <V>
. These types represent the key and value in a key-value pair, allowing
you to define objects with keys and values of any type.
Generics With Type Aliases:โ
type Callback<T> = (arg: T) => void;
In this example, the Callback
type alias is a generic function type with a
single type variable <T>
. It represents a function that takes a single
argument of type T
and returns nothing (void
). This allows you to create
callback functions with different argument types while maintaining type safety.
Real World Examples ๐โ
A real-world application of generics in type aliases can be seen in the context of a React application. Suppose you're building a blog and need a standardized way to represent the API response for a list of entities, such as posts or comments.
You can create a generic type alias to represent paginated results from the API:
type PaginatedResults<T> = {
data: T[];
currentPage: number;
totalPages: number;
};
interface Post {
id: number;
title: string;
content: string;
}
interface Comment {
id: number;
postId: number;
text: string;
}
In this example, the PaginatedResults
type alias has a single generic
type <T>
. It is used as the type for the data
property, which is an array of
items of type T
. This allows you to represent paginated results for various
types of entities, such as Post
or Comment
.
Now, when you fetch data from the API, you can define the expected response type
using the PaginatedResults
type alias:
async function fetchPosts(page: number): Promise<PaginatedResults<Post>> {
// Mock data for posts
const dummyPosts: Post[] = [
{ id: 1, title: "Dummy Post 1", content: "This is a dummy post." },
{ id: 2, title: "Dummy Post 2", content: "This is another dummy post." },
];
const paginatedResults: PaginatedResults<Post> = {
data: dummyPosts,
currentPage: 1,
totalPages: 1,
};
// Return dummy data as PaginatedResults<Post>
return paginatedResults;
}
async function fetchComments(page: number): Promise<PaginatedResults<Comment>> {
// Mock data for comments
const dummyComments: Comment[] = [
{ id: 1, postId: 1, text: "This is a dummy comment." },
{ id: 2, postId: 2, text: "This is another dummy comment." },
];
const paginatedResults: PaginatedResults<Comment> = {
data: dummyComments,
currentPage: 1,
totalPages: 1,
};
// Return dummy data as PaginatedResults<Comment>
return paginatedResults;
}
By using the PaginatedResults
generic type alias, you can ensure type safety
and maintain a consistent structure for API responses throughout your
application, regardless of the specific entities being fetched.
What Can You Do Next ๐๐โ
If you liked the article, consider subscribing to Cloudaffle, my YouTube Channel, where I keep posting in-depth tutorials and all edutainment stuff for ssoftware developers.