# TypeScript Generics Pattern > Write reusable, type-safe functions and interfaces using TypeScript generics ## When to Use - A function, class, or interface should operate on multiple types without losing type information - You are writing utility functions (identity, map, filter, pipe) that must preserve the caller's type - You want to express constraints between type parameters (e.g., "K must be a key of T") - You need to infer a return type from an argument type at the call site - You are building library or shared code that consumers will use with their own types ## Instructions 1. Declare type parameters immediately after the function name or interface keyword: `function wrap(value: T): T[]`. 2. Apply constraints with `extends` when the body requires a specific shape: `function getKey(item: T): string`. 3. Use multiple type parameters when you need to relate two types: `function map(arr: T[], fn: (item: T) => U): U[]`. 4. Provide defaults for optional type parameters: `interface Response { data: T; status: number }`. 5. Use `infer` inside conditional types to extract sub-types: `type Awaited = T extends Promise ? U : T`. 6. Avoid over-constraining — prefer the narrowest constraint that allows the body to compile. 7. Name type parameters meaningfully: `T` for a single generic, `TKey`/`TValue` for maps, `TResult` for return types. 8. Prefer generic interfaces over function overloads when the shape is consistent across types. ```typescript // Generic identity — preserves exact type at call site function identity(value: T): T { return value; } const n = identity(42); // type: 42 (literal) const s = identity('hi'); // type: "hi" (literal) // Constrained generic — body can access .length function longest(a: T, b: T): T { return a.length >= b.length ? a : b; } // Inferring the resolved type of a Promise type Awaited = T extends Promise ? U : T; type X = Awaited>; // string // Generic interface with default interface ApiResponse { data: T; error: string | null; timestamp: number; } // Relating two type parameters function mapRecord( record: Record, fn: (value: V, key: K) => W ): Record { return Object.fromEntries( Object.entries(record).map(([k, v]) => [k, fn(v as V, k as K)]) ) as Record; } ``` ## Details TypeScript's type system is structural and erased at runtime, but generics exist entirely in the type layer — they impose no runtime cost. The compiler resolves type parameters at each call site through a process called type argument inference, which means callers rarely need to supply explicit type arguments unless inference fails. **Constraints and the `extends` keyword** `extends` in a type parameter context means "is assignable to", not class inheritance. `` accepts any non-primitive. `` links two parameters. When you constrain, the body gains access to the constraint's members; when unconstrained, the body can only treat `T` as `unknown`. **Generic defaults** Type parameter defaults (``) allow callers to omit the argument. They work like function parameter defaults — the default is used when inference produces `unknown` or the caller omits it explicitly. **The `infer` keyword** `infer` appears only inside conditional type clauses (`extends ? T : F`). It introduces a fresh type variable that is bound to whatever matched that position in the extends check. Common uses: `ReturnType`, `Parameters`, `Awaited`, and custom extractors. **Variance and type safety** Generic types are covariant in their type parameters by default (not yet formally annotated in TS). This becomes relevant when storing generic values in arrays or returning them from factories — TypeScript widens to the common type unless you keep the parameter in position. **Anti-patterns to avoid** - Using `any` inside a generic body to make it compile — defeats the purpose - Unnecessary explicit type arguments (`identity("hello")` — inference handles this) - Deep constraint hierarchies that make call-site error messages unreadable - Overusing generics on simple functions that only ever operate on one type ## Source https://typescriptlang.org/docs/handbook/2/generics.html ## Process 1. Read the instructions and examples in this document. 2. Apply the patterns to your implementation, adapting to your specific context. 3. Verify your implementation against the details and edge cases listed above. ## Harness Integration - **Type:** knowledge — this skill is a reference document, not a procedural workflow. - **No tools or state** — consumed as context by other skills and agents. ## Success Criteria - The patterns described in this document are applied correctly in the implementation. - Edge cases and anti-patterns listed in this document are avoided.