MacroContext API
The MacroContext is passed to every macro's expand function.
Compiler Access
program
ctx.program: ts.ProgramThe TypeScript program being compiled.
typeChecker
ctx.typeChecker: ts.TypeCheckerThe TypeScript type checker. Use for type queries:
const type = ctx.typeChecker.getTypeAtLocation(node);
const symbol = ctx.typeChecker.getSymbolAtLocation(node);
const signature = ctx.typeChecker.getSignatureFromDeclaration(decl);sourceFile
ctx.sourceFile: ts.SourceFileThe current source file being processed.
const fileName = ctx.sourceFile.fileName;
const text = node.getText(ctx.sourceFile);factory
ctx.factory: ts.NodeFactoryThe TypeScript node factory. Use for creating AST nodes:
ctx.factory.createIdentifier("name");
ctx.factory.createNumericLiteral(42);
ctx.factory.createBinaryExpression(left, operator, right);transformContext
ctx.transformContext: ts.TransformationContextThe transformation context. Rarely needed directly.
Node Creation Helpers
createIdentifier
ctx.createIdentifier(name: string): ts.IdentifierCreate an identifier node.
const id = ctx.createIdentifier("myVariable");createStringLiteral
ctx.createStringLiteral(value: string): ts.StringLiteralCreate a string literal.
const str = ctx.createStringLiteral("hello");createNumericLiteral
ctx.createNumericLiteral(value: number): ts.NumericLiteralCreate a numeric literal.
const num = ctx.createNumericLiteral(42);createBooleanLiteral
ctx.createBooleanLiteral(value: boolean): ts.ExpressionCreate a boolean literal (true or false).
const bool = ctx.createBooleanLiteral(true);createArrayLiteral
ctx.createArrayLiteral(elements: ts.Expression[]): ts.ArrayLiteralExpressionCreate an array literal.
const arr = ctx.createArrayLiteral([ctx.createNumericLiteral(1), ctx.createNumericLiteral(2)]);createObjectLiteral
ctx.createObjectLiteral(
properties: Array<{ name: string; value: ts.Expression }>
): ts.ObjectLiteralExpressionCreate an object literal.
const obj = ctx.createObjectLiteral([
{ name: "x", value: ctx.createNumericLiteral(1) },
{ name: "y", value: ctx.createNumericLiteral(2) },
]);parseExpression
ctx.parseExpression(code: string): ts.ExpressionParse a code string into an expression AST.
const expr = ctx.parseExpression("a + b * c");parseStatements
ctx.parseStatements(code: string): ts.Statement[]Parse a code string into statements.
const stmts = ctx.parseStatements(`
const x = 1;
console.log(x);
`);Type Utilities
getTypeOf
ctx.getTypeOf(node: ts.Node): ts.TypeGet the TypeScript type of a node.
const type = ctx.getTypeOf(expression);getTypeString
ctx.getTypeString(node: ts.Node): stringGet the type as a string representation.
const typeStr = ctx.getTypeString(expr);
// e.g., "number", "string[]", "{ x: number; y: number }"isAssignableTo
ctx.isAssignableTo(source: ts.Type, target: ts.Type): booleanCheck if source type is assignable to target type.
const numberType = ctx.typeChecker.getNumberType();
const argType = ctx.getTypeOf(arg);
if (ctx.isAssignableTo(argType, numberType)) {
// arg is a number
}getPropertiesOfType
ctx.getPropertiesOfType(type: ts.Type): ts.Symbol[]Get all properties of a type.
const type = ctx.getTypeOf(classNode);
const props = ctx.getPropertiesOfType(type);
for (const prop of props) {
console.log(prop.getName());
}getSymbol
ctx.getSymbol(node: ts.Node): ts.Symbol | undefinedGet the symbol for a node.
const symbol = ctx.getSymbol(identifier);
if (symbol) {
const decls = symbol.getDeclarations();
}Diagnostics
reportError
ctx.reportError(node: ts.Node, message: string): voidReport a compile-time error.
if (!arg) {
ctx.reportError(callExpr, "Missing required argument");
return callExpr;
}reportWarning
ctx.reportWarning(node: ts.Node, message: string): voidReport a compile-time warning.
if (deprecated) {
ctx.reportWarning(node, "This API is deprecated");
}Compile-Time Evaluation
evaluate
ctx.evaluate(node: ts.Node): unknownEvaluate an expression at compile time.
const arg = callExpr.arguments[0];
const value = ctx.evaluate(arg);
// value is the JS value (number, string, object, etc.)Returns undefined if evaluation fails.
isComptime
ctx.isComptime(node: ts.Node): booleanCheck if a node can be evaluated at compile time.
if (ctx.isComptime(arg)) {
const value = ctx.evaluate(arg);
// Safe to use value
} else {
ctx.reportError(arg, "Argument must be a compile-time constant");
}Hygiene
generateUniqueName
ctx.generateUniqueName(prefix: string): ts.IdentifierGenerate a unique identifier to avoid name collisions.
const temp = ctx.generateUniqueName("temp");
// Creates something like __temp_42Use for any generated variables to ensure hygiene:
const result = ctx.generateUniqueName("result");
return quote(ctx)`
const ${result} = ${expr};
return ${result};
`;Example Usage
defineExpressionMacro("example", {
expand(ctx, callExpr) {
// Get arguments
const args = callExpr.arguments;
// Validate
if (args.length === 0) {
ctx.reportError(callExpr, "Missing argument");
return callExpr;
}
// Get type info
const argType = ctx.getTypeString(args[0]);
// Evaluate if constant
if (ctx.isComptime(args[0])) {
const value = ctx.evaluate(args[0]);
return ctx.createNumericLiteral((value as number) * 2);
}
// Generate unique name
const temp = ctx.generateUniqueName("temp");
// Build result
return quote(ctx)`
(() => {
const ${temp} = ${args[0]};
return ${temp} * 2;
})()
`;
},
});