
Understanding TypeScript Generics: What Does `T` Mean?
Confused by `<T>` in TypeScript? Learn Generics using the 'Transparent Sticker' analogy. Understand why it's safer than `any` and how to create flexible, reusable components.

Confused by `<T>` in TypeScript? Learn Generics using the 'Transparent Sticker' analogy. Understand why it's safer than `any` and how to create flexible, reusable components.
A deep dive into Robert C. Martin's Clean Architecture. Learn how to decouple your business logic from frameworks, databases, and UI using Entities, Use Cases, and the Dependency Rule. Includes Screaming Architecture and Testing strategies.

Connecting incompatible interfaces. How to make old code work with new code without rewriting.

SRP, OCP, LSP, ISP, DIP. The foundation of maintainable software architecture. Examples in TypeScript.

Code is read 10x more than it is written. Why meaningful names matter, how to split functions, and why comments are often failures. The art of refactoring.

T Mean?<T>"Learning TypeScript was fine until functions.
Then came the angle brackets <T>, turning code into alien hieroglyphs.
function echo<T>(arg: T): T {
return arg;
}
"Can't I just use any? Why complicate things?"
I labeled Generics as "only for wizards" and avoided them.
But libraries and real-world code were full of them. I had to face it.
I thought "Typing" meant "Being Explicit."
string, number. That felt safe.
Generics said, "We don't know what's coming, but it's not any."
"If you don't know, how can you check it? Isn't that just any?"
I couldn't accept this contradiction.
I understood it as "Vending Machine & Transparent Sticker."
any: A Black Box. You don't see what's inside. You put in an apple, but pull out a brick. (Dangerous)<T>): A Transparent Bag. You don't know what goes in yet, but "Whatever you put in, you clearly see as is." Put in an apple, you see a red apple.// any: Input is free, but Output is unknown (Risky)
function anyBox(item: any): any {
return item;
}
const box1 = anyBox(10); // box1 is 'any'.
// Generic: Type is captured at input moment (Safe)
function genericBox<T>(item: T): T {
return item;
}
const box2 = genericBox(10); // box2 is 'number'! (TS inferred it)
Generics are "Passing types as arguments variables at usage time." It's just a "Type Variable."
Most common use case: API Fetch wrapper. The function doesn't know the response shape, but the caller does.
async function fetchJson<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json();
}
// 'Inject' Type T when using it
interface User { name: string }
const user = await fetchJson<User>('/api/user');
// Now user.name autocompletes!
extends)Sometimes T is too broad.
"I need at least a length property!"
// T MUST extend { length: number } (like string, array)
function logLength<T extends { length: number }>(arg: T) {
console.log(arg.length);
}
logLength("hello"); // OK (string has length)
logLength([1, 2]); // OK (array has length)
logLength(10); // Error! (number has no length)
extends is the key. It guarantees Safe Freedom.
Generics are essential for reusable components.
interface SelectProps<T> {
options: T[];
value: T;
onChange: (value: T) => void;
}
// <T,> : Comma prevents arrow function JSX syntax confusion
const Select = <T,>({ options, value, onChange }: SelectProps<T>) => {
return ( ... );
};
// Usage: String Select
<Select<string> options={["A", "B"]} value="A" ... />
// Usage: Number Select
<Select<number> options={[1, 2]} value=1 ... />
Without Generics, you'd be maintaining StringSelect, NumberSelect, or drowning in any.
Partial<T>, Pick<T, K>, Record<K, T>...
These built-in Types are just Generics under the hood.
// How Partial is actually defined
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
"Loop (in) over all keys (keyof T) and add a question mark (?)."
Once you get Generics, you can create your own Utility Types ("Type Gymnastics").
infer Keyword (Unpacking Types)The final boss of Generics: infer.
Used in Conditional Types, it means "Figure out this type and store it in a variable."
This is how the ReturnType utility is built:
// If T is a function, infer its Return Type as R, and give me R. Else any.
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
function getUser() { return { name: "Kim", age: 30 }; }
// UserType automatically becomes { name: string, age: number }
type UserType = MyReturnType<typeof getUser>;
Mastering infer unlocks the ability to read complex library source code.
You can create magic like "Extract the return type of function A and use it as the argument type for function B."
I was building an internal Axios wrapper. API responses always had nested structures. Simple generics weren't enough.
Deeply nested objects lost their type info.
Types can reference themselves.
type JsonValue = string | number | boolean | null | JsonArray | JsonObject;
interface JsonObject { [key: string]: JsonValue; } // References JsonValue
interface JsonArray extends Array<JsonValue> {} // References JsonValue
// Usage
const data: JsonObject = {
user: {
posts: [ { id: 1, title: "Hello" } ] // Fully typed heavily nested data
}
};
This ensures data.user.posts[0].title is fully typed and autocompleted.
Generics are a language to describe Structure.
T)T, U, V... It looks like algebra. It's confusing.
Use meaningful names for readability.
T -> TDataR -> TResultP -> TPropsE -> TErrorfunction fetchAPI<TData, TError>(url: string) { ... }
Your teammates will thank you.
keyof)Want to write a safe getProperty function? Use keyof.
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const user = { name: "Alice", age: 25 };
getProperty(user, "name"); // OK
getProperty(user, "email"); // Error: Argument "email" is not assignable to "name" | "age".
K extends keyof T translates to "K must be one of the keys of T".
This prevents accessing undefined properties at compile time.
Pick & OmitClassic gotcha.
// 1. Mapped Type: Iterate only over K
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
// 2. Use Exclude to subtract K from all keys
type MyOmit<T, K extends keyof T> = MyPick<T, Exclude<keyof T, K>>;
Understanding this enables you to craft types like PublicUserProfile (Omit password/email) effortlessly.