Generics
Level: Beginner
- Generic function syntax with type parameters
- Generic classes and properties
- Constraints on generic types (extends)
- Multiple type parameters
- Generic array and list patterns
- Keyof and typeof with generics
- Generic utility types
- SMS API response wrapper pattern
Why This Matters
Generics helps you write safer frontend code. In ASP.NET Core projects, TypeScript makes API response shapes, form models, DOM values, and reusable helpers easier to maintain.
Write flexible, reusable code that works with any type.
The Problem
JavaScript allows many mistakes to appear only at runtime. This lesson shows how Generics lets TypeScript catch wrong values, missing properties, and unsafe assumptions before the page reaches users.
Generic Functions
// Without generics - uses any
function getFirst(arr: any[]): any {
return arr[0];
}
// With generics - type-safe
function getFirst<T>(arr: T[]): T {
return arr[0];
}
const firstNumber = getFirst<number>([1, 2, 3]); // number
const firstName = getFirst<string>(["Ravi", "Priya"]); // string
// Type inference - no need to specify <T>
const first = getFirst([1, 2, 3]); // TypeScript knows it's number
Generic Interfaces
interface ApiResponse<T> {
success: boolean;
data: T;
message: string;
}
interface Student {
id: number;
name: string;
}
const studentResponse: ApiResponse<Student> = {
success: true,
data: { id: 101, name: "Ravi" },
message: "Student fetched"
};
const studentListResponse: ApiResponse<Student[]> = {
success: true,
data: [
{ id: 101, name: "Ravi" },
{ id: 102, name: "Priya" }
],
message: "Students fetched"
};
Generic Classes
class Repository<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
findById(id: number): T | undefined {
return this.items[0]; // Simplified
}
}
interface Student {
id: number;
name: string;
}
const studentRepo = new Repository<Student>();
studentRepo.add({ id: 101, name: "Ravi" });
const students = studentRepo.getAll(); // Student[]
Constraints
// Generic with constraint
function getProperty<T extends { name: string }>(obj: T): string {
return obj.name;
}
getProperty({ name: "Ravi" }); // OK
getProperty({ id: 101 }); // Error - no name property
// Key constraint
function getKey<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const student = { id: 101, name: "Ravi" };
const name = getKey(student, "name"); // OK
getKey(student, "email"); // Error - not a key
Real API Example
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Request failed');
}
return response.json();
}
interface Student {
id: number;
name: string;
className: string;
}
// Fetch student
const student = await fetchData<Student>('/api/students/101');
// Fetch student list
const students = await fetchData<Student[]>('/api/students');
Generic with Multiple Types
interface Pair<T, U> {
first: T;
second: U;
}
const pair: Pair<string, number> = {
first: "Ravi",
second: 101
};
function swap<T, U>(pair: Pair<T, U>): Pair<U, T> {
return {
first: pair.second,
second: pair.first
};
}
SMS Example
// Generic API response handler
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
}
async function handleStudentAPI<T>(
url: string
): Promise<ApiResponse<T>> {
try {
const response = await fetch(url);
if (!response.ok) {
return {
success: false,
error: `HTTP ${response.status}`
};
}
const data = await response.json();
return {
success: true,
data: data as T
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
interface Student {
id: number;
name: string;
className: string;
}
// Fetch single student
const singleResponse = await handleStudentAPI<Student>('/api/students/101');
if (singleResponse.success) {
console.log(singleResponse.data?.name); // Student data
}
// Fetch student list
const listResponse = await handleStudentAPI<Student[]>('/api/students');
if (listResponse.success) {
console.log(listResponse.data?.length); // Array length
}
Key Takeaways
- Generics make code reusable for any type
- Type parameters with
<T> - Generic interfaces and classes
- Constraints limit which types allowed
- Useful for API responses
- Next: Working with HTML/DOM
Generics are like template parameters in C++. Define once, use for many types. API responses are perfect use case.
- Using
anyinstead of generics — Defeats type safety - Not using type inference — Explicitly specifying
<T>unnecessary - Wrong constraint — Restricts too much
- Mixing types — Using
anyinside generic
Use ChatGPT, Claude, or Copilot to go deeper on TypeScript Generics. Try these prompts:
"When use generics vs any?""How do you constrain generics?""Why useful for API responses?""Quiz me on generics"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Quick Definitions
- Generics - The main TypeScript concept explained in this lesson.
- Type - A rule that describes what kind of value is allowed.
- Interface - A contract that describes the shape of an object.
- DTO model - The frontend shape of data coming from ASP.NET Core Web API.
Common Mistakes
- Using
anyinstead of defining a useful type - Making types too complex before understanding the data
- Forgetting that API data still needs runtime validation
- Confusing JavaScript runtime behavior with TypeScript compile-time checks
- Not reusing shared types for repeated API models
Practice Task
Create a small TypeScript example using Generics. Keep it connected to a School Management System scenario.
Suggested practice:
- Define a
Student,Teacher,Attendance, orMarksmodel. - Write one function or class using that type.
- Add one intentionally wrong value and read the TypeScript error.
- Fix the type or the data shape.
- Explain the type contract in your own words.
Quick Revision
| Question | Answer |
|---|---|
| What is the main idea? | Use TypeScript to make Generics safer and clearer. |
| Where is it used? | API models, forms, DOM code, helpers, and React/Next.js apps. |
| What should beginners avoid? | Overusing any and ignoring API validation. |
| What is the best debugging habit? | Read the TypeScript error and compare it with the expected data shape. |
Generics is a TypeScript concept that improves JavaScript safety by making data shapes and function expectations clear. I would explain the problem it solves, show a small example, and mention how it helps with API-connected frontend code.
It is used in API DTOs, form models, DOM access, helper functions, React props, service classes, and reusable frontend utilities.