Unlocking Reusability with TypeScript Generics
Sep 16, 2025 am 03:14 AMTypeScript generics enable reusable, type-safe code by allowing functions, interfaces, and components to work with multiple types while preserving type information. 1. Generics use type variables like T to represent placeholder types, as seen in the identity function, preserving type safety unlike any. 2. They are ideal for structures like ApiResponse
TypeScript generics are one of the most powerful features for writing reusable, type-safe code. They allow you to create components, functions, or classes that work over a variety of types rather than a single one—without sacrificing type information. This unlocks flexibility while maintaining strong typing, which is essential in large-scale applications.

Let’s break down how generics help you build more reusable code and when to use them.
1. What Are Generics? (And Why You Need Them)
At their core, generics let you write functions or data structures that can handle different types while preserving type safety.

For example, imagine a simple identity function:
function identity(arg: any): any { return arg; }
This works—but it throws away type information. With generics, you preserve it:

function identity<T>(arg: T): T { return arg; }
Here, T
is a type variable—a placeholder for whatever type will be used when the function is called.
You can call it like this:
const num = identity(42); // type: number const str = identity("hello"); // type: string
TypeScript infers T
automatically, so you don’t always have to write identity<number>(42)
.
This may seem trivial, but it becomes critical when building utilities like caches, APIs, or state managers where types vary but behavior stays consistent.
2. Using Generics in Functions and Interfaces
Generics really shine when abstracting logic across multiple types.
Example: A Generic API Response Wrapper
Say you're handling API responses with a consistent shape:
interface ApiResponse<T> { data: T; status: number; message?: string; }
Now you can reuse this interface across different endpoints:
const userResponse: ApiResponse<User> = { data: { id: 1, name: "Alice" }, status: 200, }; const postsResponse: ApiResponse<Post[]> = { data: [{ id: 1, title: "Hello" }], status: 200, };
Your components or services can now handle ApiResponse<T>
without knowing the exact shape of T
, yet still offer full autocomplete and type checking.
3. Constraining Generics with extends
Sometimes you want flexibility—but not too much. You can restrict what types a generic accepts using extends
.
For example, suppose you want a function that extracts a property from an object, but only if it has an id
field:
function logId<T extends { id: number }>(item: T): void { console.log(item.id); }
Now this ensures any argument passed must have an id
of type number
:
logId({ id: 123, name: "Bob" }); // ? OK logId({ name: "Charlie" }); // ? Error: missing 'id'
This pattern is common in utility functions, form handlers, or serializers where you need structural guarantees.
4. Multiple Type Parameters and Default Types
You’re not limited to one generic type.
function mapObject<K, V, R>( obj: Record<K, V>, fn: (value: V) => R ): Record<K, R> { return Object.fromEntries( Object.entries(obj).map(([key, value]) => [key, fn(value)]) ) as Record<K, R>; }
Also, you can provide default types for better ergonomics:
interface QueryResult<T = unknown> { data: T; loading: boolean; }
Now QueryResult
defaults to unknown
if no type is specified, but remains flexible.
5. Reusable Components in Practice
Generics are especially useful in:
- Utility functions (e.g.,
pick
,omit
,deepClone
) - State management (e.g., actions, reducers with payload types)
- React components (e.g., forms, modals that accept generic props)
- Data fetching hooks (e.g.,
useFetch<T>()
)
Example: A generic React hook
function useFetch<T>(url: string): { data: T | null; loading: boolean } { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then(r => r.json()) .then(data => setData(data as T)) .finally(() => setLoading(false)); }, [url]); return { data, loading }; } // Usage const { data } = useFetch<User[]>("/api/users");
Here, the same hook safely handles any expected response type.
Generics might feel abstract at first, but they’re just TypeScript’s way of letting you write future-proof code. Once you start using them, you’ll find yourself duplicating less, refactoring with confidence, and catching bugs at compile time.
Basically: if you’re repeating the same logic for different types, it’s time to reach for generics.
The above is the detailed content of Unlocking Reusability with TypeScript Generics. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

ArtGPT
AI image generator for creative art from text prompts.

Stock Market GPT
AI powered investment research for smarter decisions

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Generic functions in Go solve the problem of variadic types: generic functions allow type parameters to be specified at runtime. This makes it possible to write functions that can handle parameters of different types. For example, the Max function is a generic function that accepts two comparable parameters and returns the larger value. By using generic functions, we can write more flexible and general code that can handle different types of parameters.

Application scenarios of generics in Go: Collection operations: Create collection operations suitable for any type, such as filtering. Data Structures: Write general-purpose data structures such as queues, stacks, and maps to store and manipulate various types of data. Algorithms: Write general-purpose algorithms such as sorting, search, and reduction that can handle different types of data.

The impact of generics on Go function signatures and parameters includes: Type parameters: Function signatures can contain type parameters, specifying the types that the function can use. Type constraints: Type parameters can have constraints that specify conditions that they must satisfy. Parameter type inference: The compiler can infer the type of unspecified type parameters. Specifying types: Parameter types can be explicitly specified to call generic functions. This increases code reusability and flexibility, allowing you to write functions and types that can be used with multiple types.

The combination of enumeration types and generics in Java: When declaring an enumeration with generics, you need to add angle brackets, and T is the type parameter. When creating a generic class, you also need to add angle brackets, T is a type parameter that can store any type. This combination improves code flexibility, type safety, and simplifies code.

Java function generics allow setting upper and lower bounds. Extends specifies that the data type accepted or returned by a function must be a subtype of the specified type, e.g. The lower bound (super) specifies that the data type accepted or returned by a function must be a supertype of the specified type, e.g. The use of generics improves code reusability and security.

Limitations of Go generic functions: only type parameters are supported, value parameters are not supported. Function recursion is not supported. Type parameters cannot be specified explicitly, they are inferred by the compiler.

The difference between templates and generics in C++: Templates: defined at compile time, clearly typed, high efficiency, and small code size. Generics: runtime typing, abstract interface, provides flexibility, low efficiency.

Java generic methods automatically infer type parameters without explicit declaration. Rules include: 1. Use explicit type declarations; 2. Infer a single type; 3. Infer wildcard types; 4. Infer constructor return value types. This simplifies the code, making it easier to write and use generic methods.
