TypeScript generics explained without the jargon
TypeScript generics let one function work with many types without losing type safety. Learn the T, inference, constraints, and Partial with examples.

What is the T in function first<T>(items: T[]), and why does TypeScript code seem to be full of angle brackets?
Short answer: a generic is a type placeholder. T stands for "whatever type you pass in". It lets one function or type work with numbers, strings or users, while TypeScript still remembers exactly which one you used.
By the end of this post you will be able to read and write a generic function, know when TypeScript fills in T for you, limit what T can be, and use the built-in generics you have probably already seen. If types themselves are new, TypeScript vs JavaScript is the place to start.
The problem generics solve
Say you want a function that returns the first item of an array. For numbers:
function firstNumber(items: number[]): number | undefined {
return items[0]
}Now you need the same thing for strings. You could copy the function and change the type, but then you need another for users, another for products, and so on.
The tempting shortcut is any:
function first(items: any[]): any {
return items[0]
}
const firstName = first(["Ada", "Grace"])
firstName.toFixed(2) // no error, but crashes when it runsany turns off checking. TypeScript no longer knows that firstName is a string, so it lets you call a number method on it. You have one function again, but you lost the reason you were using TypeScript.
Your first generic function
A generic keeps the single function and the type safety:
function first<T>(items: T[]): T | undefined {
return items[0]
}Read it out loud: "first works with some type T. It takes an array of T and returns a T, or undefined if the array is empty."
The <T> after the function name declares the placeholder. Everywhere else, T means the same type. Now TypeScript keeps track:
const firstName = first(["Ada", "Grace"]) // string | undefined
const firstScore = first([90, 75, 82]) // number | undefined
firstName?.toFixed(2)
// Error: Property 'toFixed' does not exist on type 'string'.One function, any type, and mistakes still get caught. Generic Functions (opens in a new tab) walks through this step by step.
You usually don't write the type yourself
Notice that neither call above says what T is. TypeScript looks at the argument, sees an array of strings, and fills in T as string. That is inference, the same feature that knows let count = 0 is a number.
You can still write it explicitly when TypeScript has nothing to infer from, like an empty array:
const nothing = first<string>([]) // string | undefinedMost of the time, leave it out and let TypeScript work it out.
Generics you have already used
If you have written any TypeScript, you have probably used generics without calling them that.
| You have seen | What T is | What it means |
|---|---|---|
Array<string> | string | An array of strings, the same as string[] |
Promise<User> | User | A promise that resolves to a User |
useState<string[]>([]) | string[] | React state that holds an array of strings |
Map<string, number> | string and number | Keys are strings, values are numbers |
The angle brackets are always the same idea: "this container, filled with this type". Promise<User> is why await gives you a User instead of a mystery value. JavaScript async/await explained covers the JavaScript side of promises.
Generic types, not just functions
You can put a placeholder in a type, too. A common example is the shape of an API response, where the wrapper is always the same but the data changes:
type ApiResponse<T> = {
data: T
error: string | null
}
type User = { id: number; name: string }
const userResponse: ApiResponse<User> = {
data: { id: 1, name: "Ada" },
error: null,
}
const listResponse: ApiResponse<User[]> = {
data: [],
error: null,
}One type describes every response. userResponse.data.name is a string, and listResponse.data is an array of users. See Generic Object Types (opens in a new tab).
Constraints: limiting what T can be
Sometimes your function needs T to have something specific. This one returns the longer of two values:
function longest<T>(a: T, b: T): T {
return a.length >= b.length ? a : b
// Error: Property 'length' does not exist on type 'T'.
}TypeScript is right to complain. T could be a number, and numbers don't have a length. Add a constraint with extends:
function longest<T extends { length: number }>(a: T, b: T): T {
return a.length >= b.length ? a : b
}
longest("cat", "horse") // "horse"
longest([1, 2], [1, 2, 3]) // [1, 2, 3]
longest(10, 20)
// Error: Argument of type 'number' is not assignable to parameter of type '{ length: number; }'.T extends { length: number } means "any type, as long as it has a numeric length". Strings and arrays qualify. Numbers don't. Generic Constraints (opens in a new tab) has more examples.
Built-in helpers: Partial, Pick and Omit
TypeScript ships with generic types that build new types from old ones. The one you will reach for first is Partial, which makes every property optional. It is perfect for updates, where you only send the fields that changed:
type User = { id: number; name: string; email: string }
function updateUser(user: User, changes: Partial<User>): User {
return { ...user, ...changes }
}
updateUser(currentUser, { email: "ada@example.com" }) // fine
updateUser(currentUser, { emial: "ada@example.com" }) // error: no such propertyPick<User, "id" | "name"> keeps only the listed properties, and Omit<User, "email"> keeps everything except them. See Partial (opens in a new tab) and Pick And Omit (opens in a new tab).
Common mistakes
- Using
anyinstead of a generic. If the return type depends on the input type, you want a generic.anythrows that connection away. - Adding a generic that does nothing. If
Tappears only once, as infunction log<T>(value: T): void, it isn't connecting anything.value: unknownsays the same thing more simply. A generic earns its place when it links an input to an output, or two inputs to each other. - Writing the type when inference would do.
first<string>(["a", "b"])works, butfirst(["a", "b"])is shorter and just as safe. - Accessing properties on a bare
T. Without a constraint, TypeScript assumesTcould be anything. Addextendsto say what it must have. - Generic arrow functions in
.tsxfiles.const first = <T>(items: T[]) => items[0]can be mistaken for a JSX tag. Write<T,>with a trailing comma, or use a regularfunction.
Quick recap
- A generic is a type placeholder, usually called
T. - It lets one function or type work with many types while keeping type safety.
- TypeScript usually infers
Tfrom the arguments. extendslimits whatTcan be.Array,Promise,Partial,PickandOmitare all generics you can use today.
Frequently asked questions
What are generics in TypeScript?
They are placeholders for types. A generic function or type takes a type as a kind of parameter, so the same code can work with strings, numbers or objects while TypeScript still knows which one you are using.
What does T mean in TypeScript?
T is just a name for the placeholder, short for "type". You could call it anything. T is the convention for one placeholder, and you will often see K and V for keys and values, or longer names like TItem.
What is the difference between generics and any?
any turns off checking, so TypeScript forgets what the value is. A generic remembers. With first(names), a generic version knows the result is a string, and the any version knows nothing.
When should I use generics?
When the same logic works for several types and the output type depends on the input type. Helper functions for arrays, API response wrappers and reusable components are the usual places.
What does extends mean in a generic?
It is a constraint. T extends { length: number } means T can be any type that has a numeric length property. It lets you use that property inside the function.
Practice
The matching exercises are in the course. Write your first one in Generic Functions (opens in a new tab), add limits in Generic Constraints (opens in a new tab), then put it together in Project Plant Nursery (opens in a new tab). The final project uses one too, in Tracker Generic Helper (opens in a new tab). Each exercise works in your browser, so there is nothing to install.
Keep going
TypeScript Fundamentals (opens in a new tab) takes you from your first annotation to generics and utility types. If plain JavaScript functions and arrays still feel shaky, Intermediate JavaScript (opens in a new tab) is the better first step.








