Suggest an editImprove this articleRefine the answer for “What is generic in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Generics** in TypeScript are type parameters (`<T>`) that let you write reusable functions, classes, and interfaces that work with any type while preserving full type safety. ```typescript function first<T>(arr: T[]): T | undefined { return arr[0]; } const num = first([1, 2, 3]); // number const str = first(['a', 'b']); // string ``` **Key point:** Unlike `any`, generics pass the exact type through, TypeScript still checks it at every call site.Shown above the full answer for quick recall.Answer (EN)Image**Generics** in TypeScript are type parameters that let you write a function, class, or interface once and reuse it for any type, while TypeScript still enforces full type safety at each call site. ## Theory ### TL;DR - Generics are like a mold: define the shape once (`<T>`), fill it with any type at use, get full type checking for that type - Main difference: `any` throws away type info; generics pass the exact type through - TypeScript infers `T` automatically in most cases, no explicit `<string>` needed at call sites - Use generics when the same logic applies to 2+ types; use concrete types for single-type code - Constraints like `T extends object` narrow what types are allowed ### Quick example ```typescript // Without generics: loses type safety function first(arr: any[]): any { return arr[0]; } const val = first([1, 2, 3]); // val is 'any', no autocomplete, no checks // val.toUpperCase(); // TypeScript silent, but crashes at runtime // With generics: type flows through function first<T>(arr: T[]): T | undefined { return arr[0]; } const num = first([1, 2, 3]); // num is number const str = first(['a', 'b']); // str is string // num.toUpperCase(); // Compile error, TypeScript catches it ``` `T` is inferred from the argument. You rarely need to write `first<number>([1, 2, 3])` explicitly. ### Key difference The difference between generics and `any` is not just style. With `any`, TypeScript stops checking entirely: no autocomplete, no error detection. With generics, TypeScript substitutes the actual type at each call site. Call `first([1, 2, 3])` and TypeScript treats every `T` in that function as `number`. The type flows through rather than disappears. ### When to use - Same logic, different types (array operations, data wrappers, API helpers): generics - One specific type only: use that concrete type directly - Multiple input types, same output shape: generics with constraints (`T extends {id: string}`) - Shared utilities across the codebase: generics (callers pick their types) - Only 2-3 known variants: overloads can be simpler ### How TypeScript handles generics TypeScript treats `T` as an abstract placeholder during declaration, then performs type instantiation at each call site. This happens entirely at compile time. The emitted JavaScript has no trace of generics. For `T extends string`, the compiler narrows possible substitutions before generating JS. No runtime cost, similar to Java type erasure. ### Common mistakes **Mistake: mutating generic input assuming `any`-like freedom** ```typescript // Wrong function append<T>(arr: T[]): T[] { arr.push('extra' as any); // Breaks the caller's T[] return arr; } // Right function append<T>(arr: T[], item: T): T[] { return [...arr, item]; // Caller controls the type } ``` `T` is fixed at the call site. Pushing a `string` into a `T[]` where `T` is `number` breaks callers even if TypeScript misses it behind a cast. **Mistake: forgetting constraints on object operations** ```typescript // Wrong, T may be a primitive and spread fails function merge<T>(a: T, b: T) { return { ...a, ...b }; } // Right function merge<T extends object>(a: T, b: T) { return { ...a, ...b }; } ``` **Mistake: no arguments to drive inference** ```typescript // Wrong, T inferred as never[] function createArray<T>(): T[] { return []; } const arr = createArray(); // never[] // Right, parameter drives inference function createArray<T>(size: number, fill: T): T[] { return Array(size).fill(fill); } const nums = createArray(3, 0); // number[] ``` ### Real-world usage - React: `useRef<HTMLDivElement>()` returns `RefObject<HTMLDivElement>`, exact DOM type - React state: `useState<User | null>(null)` is typed from the start, not `any` - TanStack Query: `useQuery<User[]>` propagates types through every selector - Lodash types: `_.map<T, R>(arr: T[], fn: (x: T) => R): R[]` links input and output types - Express: `Request<P, ResBody, ReqBody>` for typed route handlers ### Follow-up questions **Q:** What is the difference between `T extends object` and `T extends {}`? **A:** `T extends {}` matches everything except `null` and `undefined`, including primitives. `T extends object` also excludes primitives. For spread operations you want `T extends object`. **Q:** Write a function that picks properties from an object by key array. **A:** ```typescript function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> { return keys.reduce((acc, k) => ({ ...acc, [k]: obj[k] }), {} as Pick<T, K>); } ``` `K extends keyof T` ensures only valid keys are passed. TypeScript catches `pick(user, ['salary'])` if `salary` is not on the type. **Q:** How are TypeScript generics different from Java generics at runtime? **A:** TypeScript generics are fully erased at compile time, the JS output has no type info at all. Java has reified generics in some cases, allowing runtime checks like `instanceof`. That is not possible in TypeScript. **Q:** What does `infer` do inside conditional types? **A:** ```typescript type UnpackPromise<T> = T extends Promise<infer U> ? U : T; // UnpackPromise<Promise<string>> = string // UnpackPromise<number> = number ``` `infer U` tells TypeScript to extract the inner type from `Promise<...>` and bind it to `U` for the true branch. This is how utility types like `Awaited<T>` are built. **Q:** Junior vs senior on bounded generics: constrain to only strings and numbers. **A:** Junior writes `T extends string | number` and that works. A senior knows the limits: if you need different behavior per type, overloads or conditional types (`T extends string ? string : number`) give you more control. ## Examples ### Basic: identity function with type inference ```typescript function identity<T>(arg: T): T { return arg; } const num = identity(42); // T inferred as number const str = identity('hello'); // T inferred as string const obj = identity({ id: 1 }); // T inferred as { id: number } // num.toUpperCase(); // Compile error, TypeScript knows it's number console.log(num.toFixed(2)); // "42.00" ``` TypeScript infers `T` from the argument. Writing `identity<number>(42)` works but is not required. ### Intermediate: typed property access with two type parameters ```typescript function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } interface Person { name: string; age: number; } const person: Person = { name: 'Alice', age: 30 }; console.log(getProp(person, 'name')); // "Alice", return type is string console.log(getProp(person, 'age')); // 30, return type is number // getProp(person, 'salary'); // Compile error: 'salary' is not in Person ``` Two type parameters working together: `T` is the object, `K` is constrained to keys of `T`. The return type `T[K]` is the exact property type. No casting, no `any`. ### Advanced: generic data fetching hook ```typescript // Pattern common in TanStack Query and custom fetch wrappers function useData<T>(url: string): { data: T | null; loading: boolean } { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then(res => res.json()) .then(setData) .finally(() => setLoading(false)); }, [url]); return { data, loading }; } interface User { id: number; name: string; } // T = User[], so data is User[] | null const { data: users, loading } = useData<User[]>('/api/users'); if (users) { console.log(users[0].name); // Safe, TypeScript knows the shape } ``` The hook has no idea about `User`. The caller decides the type at use. Every project I have worked on with a custom fetch wrapper eventually reached this pattern: add one `<T>` and the whole component tree gets typed automatically.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.