Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A generic function in TypeScript is a function parameterized over types. It declares one or more type variables (type parameters) enclosed in angle brackets (< >) immediately preceding the function’s parameter list. This mechanism allows the function to capture the specific types provided by the caller, establishing a strict, statically-analyzed relationship between the input parameters, internal logic, and the return type without sacrificing type safety or resorting to the any type.

Syntax Declarations

The placement of the type parameter varies slightly depending on how the function is declared.
// Standard function declaration
function process<T>(payload: T): T {
    return payload;
}

// Function expression
const processExpr = function <T>(payload: T): T {
    return payload;
};

// Arrow function
const processArrow = <T>(payload: T): T => {
    return payload;
};

// Method within an object or class
class Processor {
    processMethod<T>(payload: T): T {
        return payload;
    }
}
Note: When writing generic arrow functions in .tsx files, the compiler can confuse the <T> syntax with a JSX element. To resolve this parser ambiguity, append a trailing comma to the type parameter: <T,>(payload: T) => payload;

Type Parameters vs. Type Arguments

  • Type Parameter: The placeholder variable declared in the function signature (e.g., the T in <T>).
  • Type Argument: The concrete type bound to the parameter during invocation (e.g., the string in process<string>("data")).

Type Inference

TypeScript’s compiler utilizes type argument inference to automatically determine the type argument based on the runtime arguments passed to the function. Explicitly passing the type argument is only required when the compiler lacks sufficient context to infer it correctly.
function extract<T>(items: T[]): T {
    return items[0];
}

// Explicit type argument binding
const a = extract<number>([1, 2, 3]); 

// Implicit type argument inference (T is inferred as 'string')
const b = extract(["x", "y", "z"]); 

Multiple Type Parameters

Functions can declare multiple type parameters by separating them with commas. This is used to establish relationships between distinct types within the same function signature.
function merge<T, U>(first: T, second: U): T & U {
    return { ...first, ...second };
}

// T is inferred as { id: number }, U is inferred as { name: string }
const mergedObj = merge({ id: 1 }, { name: "Alice" });

Generic Constraints

By default, an unconstrained type parameter can accept any type. To safely access specific properties or methods on a generic parameter, you must constrain it using the extends keyword. This enforces that the type argument satisfies a specific structural contract.
interface Lengthwise {
    length: number;
}

// T is constrained; it must possess a 'length' property of type 'number'
function logLength<T extends Lengthwise>(arg: T): number {
    return arg.length; // Safe: compiler knows 'length' exists
}

logLength({ length: 10, value: "data" }); // Valid
// logLength(42); // Error: number does not have a 'length' property

Type Parameter Defaults

Type parameters can be assigned default types. If the compiler cannot infer the type from the invocation context and no explicit type argument is provided, it will fall back to the default type.
function createNode<T = string>(value?: T): { data: T | undefined } {
    return { data: value };
}

// T is inferred as 'number' from the argument
const node1 = createNode(42); 

// No argument provided, T defaults to 'string'
const node2 = createNode(); 

Constraining Types by Other Type Parameters

A type parameter can be constrained by another type parameter declared in the same function signature. This is frequently used to enforce that a parameter is a valid key of another parameter’s type using the keyof operator.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

const user = { id: 1, role: "admin" };

getProperty(user, "role"); // Valid: "role" is a key of 'user'
// getProperty(user, "email"); // Error: Argument of type '"email"' is not assignable to parameter of type '"id" | "role"'.
Master TypeScript with Deep Grasping Methodology!Learn More