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.
Type-Position Macros
A type-position macro rewrites TypeScript types rather than values. It’s invoked with angle brackets:
const $Nullable = macroRules({
kind: "type",
expand: macroRules`
($t:Type) => $t | null | undefined
`,
});
type MaybeUser = $Nullable<User>;
// → type MaybeUser = User | null | undefined;Requirements
The object form is mandatory. A tag-form macroRules`…` always declares a value macro; there is no way to mark it as a type macro. You must use macroRules({ kind: "type", expand: … }).
Only Type and Tt fragment kinds are accepted in type-position patterns. Any other kind produces a dedicated error.
Zero-argument references
A bare $Foo with no type arguments matches only an arm whose pattern is ():
const $Id = macroRules({
kind: "type",
expand: macroRules`
() => string
($t:Type) => $t
`,
});
type A = $Id; // → string
type B = $Id<number>; // → numberQualified names never resolve — a.$Foo is not a macro reference.
Sharing modes are rejected
Types have no runtime representation, so a type macro cannot share a runtime helper. Setting mode to share-only or share-anyway with kind: "type" is an error — and so is supplying runtime or call at all, independently of the mode.
Cross-position misuse
The two directions behave differently:
- Using a value macro in type position is a hard error: “macro
$Xis value-only; cannot use it in type position”. - Using a type macro in value position falls through to the ordinary no-arm-matched path, since the value-position matcher never sees its type arms.
No backtracking
Type-position repetition matching is greedy with no retry. A pattern like ($($t:Type),* $last:Type) that works in value position will never match in type position. Keep the variable-length group last, or write one arm per arity. See Repetitions & Backtracking.
Composition
Type macros compose with each other exactly like value macros, and nested expansions run in type context:
const $Wrap = macroRules({
kind: "type",
expand: macroRules`($t:Type) => Array<$Nullable<$t>>`,
});