TypeScript Generics for Beginners
SkillVeris Team
Engineering Team

Generics let a function, class, or type work with many types while keeping full type safety, using a placeholder type variable like T.
In this guide, you'll learn:
- They replace the loose any type: a generic remembers the specific type passed in and preserves it through the code.
- The type parameter is inferred from arguments most of the time, so you rarely have to specify it explicitly.
- Constraints with extends limit a generic to types that have required properties, like T extends { id: number }.
- Generics power everyday tools: arrays, promises, and utility types like Partial and Record are all generic.
1What Are TypeScript Generics?
Generics let you write a function, class, or type that works with many different types while preserving type safety, using a placeholder called a type parameter — conventionally T. Instead of locking a function to one type or falling back to the unsafe any, a generic captures whatever type is passed in and carries it through.
Think of a type parameter as an argument for types rather than values. When you call a generic function with a string, T becomes string for that call; with a number, T becomes number. The compiler tracks that relationship, so the return type stays accurate.
2The Problem Generics Solve
Without generics you face a bad choice: write the same function for every type, or use any and lose all type checking. A function that returns the first element of an array shows the difference.
- function first(arr: any[]): any # returns any — no safety, autocomplete gone
- function first<T>(arr: T[]): T # returns the array's element type
- first([1, 2, 3]) is typed number; first(['a']) is typed string.
- The caller keeps full type information without writing a version per type.
🔑Generics Beat any
any turns off the type checker. A generic keeps it on — it remembers the real type instead of forgetting it, so you keep autocomplete and error catching.
3Writing Generic Functions
You declare a type parameter in angle brackets after the function name, then use it in the parameters and return type. The compiler usually infers T from the arguments, so calls stay clean.
- function identity<T>(value: T): T { return value; }
- identity('hello') # T inferred as string, no explicit type needed
- identity<number>(42) # you can specify T explicitly when helpful
- Multiple params: function pair<A, B>(a: A, b: B): [A, B]
💡Let Inference Work
Most of the time you do not write the type argument at all — TypeScript infers it from what you pass. Only specify it when inference cannot figure it out.
4Constraining Generics
Sometimes a generic should not accept literally any type — it needs certain properties to exist. The extends keyword constrains a type parameter to types that satisfy a shape, so you can safely access those properties inside.
Requiring a Property
A constraint guarantees the property is present, so the compiler lets you use it. Without the constraint, accessing item.id would be an error because T might not have it.
function getId<T extends { id: number }>(item: T): number {
return item.id; # safe because T is guaranteed to have id
}Keyof Constraints
Constraining one parameter to the keys of another creates precise helpers, like a type-safe property getter that only accepts real keys of the object.
function get<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]; # return type is exactly the property's type
}5Generics You Already Use
Generics are not an advanced corner of TypeScript — they underpin tools you use daily. Recognizing them makes the concept click faster.
- Array<string> is just a generic; string[] is shorthand for it.
- Promise<User> describes a promise that resolves to a User.
- React's useState<number>(0) types the state value.
- Utility types: Partial<T>, Readonly<T>, Record<K, V>, and Pick<T, K> are all generic.
- Map<string, User> ties key and value types together.
6Common Mistakes to Avoid
Generics are approachable once the mental model lands, but beginners hit a few predictable snags.
- Reaching for generics when a single concrete type would be clearer — do not add T for its own sake.
- Falling back to any inside a generic function, which quietly discards the safety you gained.
- Forgetting constraints, then getting errors when you access properties T might not have.
- Over-parameterizing with many type variables that make signatures hard to read.
- Specifying the type argument manually when inference would handle it fine.
⚠️Do Not Overuse Generics
A generic earns its place when input and output types must vary together. If a function only ever handles one type, a plain type is simpler and clearer.
7Generic Types and Components
Generics are not just for functions. You can make your own types and React components generic, so a reusable structure adapts to whatever data it holds while staying type-safe.
A generic type like ApiResponse<T> describes a wrapper whose data field varies, and a generic component like List<T> renders items of any type while typing its render callback correctly. This is how well-designed component libraries stay flexible without resorting to any.
- type ApiResponse<T> = { data: T; status: number } # a reusable generic type
- type Box<T> = { value: T } # varies by what it wraps
- function List<T>({ items, render }: { items: T[]; render: (item: T) => ReactNode })
- Calling List with User[] types the render callback's item as User automatically.
💡Generic Components
A generic component keeps a shared List or Table type-safe for every data type, so consumers get autocomplete on each item instead of any.
8Key Takeaways
Generics are the tool that keeps reusable code type-safe. These points summarize what beginners need.
- A generic uses a type parameter like T to work with many types while staying type-safe.
- They replace any by remembering the specific type instead of discarding it.
- TypeScript usually infers the type parameter, so you rarely specify it.
- Constraints with extends require a type to have certain properties before you use them.
- Arrays, promises, and utility types like Partial and Record are all generics you already rely on.
9Frequently Asked Questions
Q: What does the T in generics mean? A: T is just a conventional name for a type parameter — a placeholder for a type that gets filled in when the function or type is used. You can name it anything, and multiple parameters often use T, U, or descriptive names like TValue.
Q: How are generics different from using any? A: any turns off type checking entirely, so you lose autocomplete and error detection. A generic keeps the checker on by remembering the exact type passed in and preserving it through the code, giving you both flexibility and safety.
Q: Do I always have to specify the type parameter? A: No. TypeScript infers the type parameter from the arguments most of the time, so calls like identity('hi') just work. You only specify it explicitly, like identity<number>(42), when inference cannot determine it on its own.
Q: What is a generic constraint? A: A constraint, written with extends, limits a type parameter to types that satisfy a shape, such as T extends { id: number }. It lets you safely access required properties inside the function, because the compiler knows every T will have them.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering writers turn abstract code concepts into hands-on, project-driven learning experiences.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.