API Stability Notice

Macroforge is under active development. The API is not yet stable and may change between versions. Some documentation sections may be outdated.

Your First Macro

Let's create a class that uses Macroforge's derive macros to automatically generate useful methods.

Creating a Class with Derive Macros

Start by creating a simple User class. We'll use the @derive decorator to automatically generate methods.

Before (Your Code)
TypeScript
/** @derive(Debug, Clone, Eq) */
export class User {
    name: string;
    age: number;
    email: string;

    constructor(name: string, age: number, email: string) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}
After (Generated)
TypeScript
/**  */
export class User {
    name: string;
    age: number;
    email: string;

    constructor(name: string, age: number, email: string) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    toString(): string {
        const parts: string[] = [];
        parts.push('name: ' + this.name);
        parts.push('age: ' + this.age);
        parts.push('email: ' + this.email);
        return 'User { ' + parts.join(', ') + ' }';
    }

    clone(): User {
        const cloned = Object.create(Object.getPrototypeOf(this));
        cloned.name = this.name;
        cloned.age = this.age;
        cloned.email = this.email;
        return cloned;
    }
}

Using the Generated Methods

TypeScript
const user = new User("Alice", 30, "[email protected]");

// Debug: toString()
console.log(user.toString());
// Output: User { name: Alice, age: 30, email: [email protected] }

// Clone: clone()
const copy = user.clone();
console.log(copy.name); // "Alice"

// Eq: equals()
console.log(user.equals(copy)); // true

const different = new User("Bob", 25, "[email protected]");
console.log(user.equals(different)); // false

Customizing Behavior

You can customize how macros work using field-level decorators. For example, with the Debug macro:

Before (Your Code)
TypeScript
/** @derive(Debug) */
export class User {
    /** @debug({ rename: "userId" }) */
    id: number;

    name: string;

    /** @debug({ skip: true }) */
    password: string;

    constructor(id: number, name: string, password: string) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
}
After (Generated)
TypeScript
/**  */
export class User {
    id: number;

    name: string;

    password: string;

    constructor(id: number, name: string, password: string) {
        this.id = id;
        this.name = name;
        this.password = password;
    }

    toString(): string {
        const parts: string[] = [];
        parts.push('userId: ' + this.id);
        parts.push('name: ' + this.name);
        return 'User { ' + parts.join(', ') + ' }';
    }
}
TypeScript
const user = new User(42, "Alice", "secret123");
console.log(user.toString());
// Output: User { userId: 42, name: Alice }
// Note: 'id' is renamed to 'userId', 'password' is skipped
Field-level decorators
Field-level decorators let you control exactly how each field is handled by the macro.

Next Steps