SyntaxStudy
Sign Up
TypeScript Higher-Order Functions and this Typing
TypeScript Beginner 1 min read

Higher-Order Functions and this Typing

Higher-order functions take other functions as arguments or return functions as results. TypeScript can fully type these patterns, including curried functions, function factories, and decorator-style wrappers. Typing higher-order functions often requires generic parameters to propagate type information from input to output without losing specificity. The this parameter is a special pseudo-parameter that TypeScript recognises at the first position in a function definition. It lets you declare what type this must be when the function is called. This prevents common bugs where methods are called in the wrong context. The this parameter is erased at compile time and does not appear in the compiled JavaScript. Function composition is a powerful functional programming pattern where small single-purpose functions are combined into more complex ones. TypeScript's type system can express typed composition pipelines using generic constraints and conditional types, though deeply nested compositions require careful type design to keep the compiler happy.
Example
// Higher-order function — map with generics
function mapArray<T, U>(arr: T[], fn: (item: T, index: number) => U): U[] {
    return arr.map(fn);
}
const doubled = mapArray([1, 2, 3], x => x * 2); // number[]
const lengths = mapArray(["hi", "hello"], s => s.length); // number[]

// Currying
function curry<A, B, C>(fn: (a: A, b: B) => C): (a: A) => (b: B) => C {
    return (a) => (b) => fn(a, b);
}

const curriedAdd = curry((a: number, b: number) => a + b);
const add5 = curriedAdd(5);
console.log(add5(3)); // 8

// 'this' parameter typing
interface Counter {
    count: number;
    increment(this: Counter): void;
    reset(this: Counter): void;
}

const counter: Counter = {
    count: 0,
    increment() { this.count++; },
    reset()     { this.count = 0; },
};

// Memoisation wrapper preserving function signature
function memoize<Args extends unknown[], R>(
    fn: (...args: Args) => R
): (...args: Args) => R {
    const cache = new Map<string, R>();
    return (...args) => {
        const key = JSON.stringify(args);
        if (!cache.has(key)) cache.set(key, fn(...args));
        return cache.get(key)!;
    };
}

const expensiveSqrt = memoize((n: number) => Math.sqrt(n));
console.log(expensiveSqrt(144)); // 12