SyntaxStudy
Sign Up
TypeScript Callable and Constructable Interfaces
TypeScript Beginner 1 min read

Callable and Constructable Interfaces

Interfaces can describe not just object shapes but also functions and constructors. A callable interface has a call signature — a member with no name, written like a function signature. This allows you to type functions that also have properties, which is a common pattern in JavaScript for library APIs. A construct signature uses the new keyword inside an interface to describe a type that can be called with new. This is useful when you want to accept a class constructor as a parameter and ensure that the constructed instance matches a specific shape. The pattern enables dependency injection and factory patterns in a fully type-safe way. These advanced interface features allow TypeScript to model virtually any JavaScript pattern. Understanding callable and constructable interfaces is particularly relevant when working with higher-order functions, plugin systems, and class-based frameworks where classes themselves are passed around as values.
Example
// Callable interface — function with properties
interface Logger {
    (message: string): void;       // call signature
    level: "info" | "warn" | "error";
    prefix: string;
}

function createLogger(prefix: string): Logger {
    const log = (message: string) => {
        console.log(`[${log.level.toUpperCase()}] ${log.prefix}: ${message}`);
    };
    log.level = "info";
    log.prefix = prefix;
    return log as Logger;
}

const appLog = createLogger("App");
appLog("Server started");
appLog.level = "warn";
appLog("Memory usage high");

// Construct signature — accept a class constructor
interface Constructable<T> {
    new (id: number, name: string): T;
}

interface Entity {
    id: number;
    name: string;
}

function createEntity<T extends Entity>(
    Ctor: Constructable<T>,
    id: number,
    name: string
): T {
    return new Ctor(id, name);
}

class Product implements Entity {
    constructor(public id: number, public name: string) {}
    describe(): string { return `Product #${this.id}: ${this.name}`; }
}

const p = createEntity(Product, 101, "Widget");
console.log(p.describe());