HomeGuidesTypeScriptTypeScript Generics Explained — Generic Functions, Constraints & keyof
🔷 TypeScript

TypeScript Generics: Functions, Interfaces, and Constraints

Generics enable reusable, type-safe code. Here's what interviews test — from basic generic functions to constraints.

Examifyr·2026·6 min read

Generic functions

Generics create functions that work with multiple types while preserving type information.

// Without generics — loses type information
function identity(value: any): any {
    return value;
}

// With generics — type is preserved
function identity<T>(value: T): T {
    return value;
}

const str = identity("hello");    // type: string
const num = identity(42);         // type: number

// Multiple type parameters
function pair<A, B>(first: A, second: B): [A, B] {
    return [first, second];
}

const result = pair("Alice", 30);  // type: [string, number]

Generic interfaces and types

Generics also work with interfaces and type aliases.

// Generic interface
interface ApiResponse<T> {
    data: T;
    status: number;
    message: string;
}

type UserResponse    = ApiResponse<User>;
type UserListResponse = ApiResponse<User[]>;

// Generic Stack
interface Stack<T> {
    push(item: T): void;
    pop(): T | undefined;
    peek(): T | undefined;
    isEmpty(): boolean;
}

// Generic Promise wrapper
async function fetchJson<T>(url: string): Promise<T> {
    const res = await fetch(url);
    return res.json() as T;
}

Constraints with extends

Constraints restrict which types can be used as a type parameter.

// T must have a length property
function longest<T extends { length: number }>(a: T, b: T): T {
    return a.length >= b.length ? a : b;
}

longest("hello", "hi");        // OK — strings have length
longest([1, 2, 3], [1, 2]);    // OK — arrays have length
longest(1, 2);                  // Error — numbers have no length

// keyof constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user = { name: "Alice", age: 30 };
getProperty(user, "name");   // type: string
getProperty(user, "age");    // type: number
getProperty(user, "email");  // Error: "email" not in user
Note: The K extends keyof T pattern is very common and frequently tested. It creates a type-safe property accessor.

Conditional types

Conditional types express "if T extends X, then Y, else Z" at the type level.

type IsArray<T> = T extends any[] ? true : false;

type A = IsArray<number[]>;  // true
type B = IsArray<string>;    // false

// NonNullable removes null and undefined
type NonNullable<T> = T extends null | undefined ? never : T;
type C = NonNullable<string | null>;  // string

// Extracting return type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type D = ReturnType<() => string>;  // string

Exam tip

The K extends keyof T pattern is the most common advanced TypeScript interview question. It creates a function that accepts only valid property names — TypeScript guarantees you can't access a property that doesn't exist.

🎯

Think you're ready? Prove it.

Take the free TypeScript readiness test. Get a score, topic breakdown, and your exact weak areas.

Take the free TypeScript test →

Free · No sign-up · Instant results

← Previous
TypeScript Union & Intersection Types — Discriminated Unions Explained
Next →
TypeScript Utility Types — Partial, Required, Pick, Omit, Record
← All TypeScript guides