reflect Module
The reflect module provides runtime TypeScript/JavaScript analysis and introspection capabilities, allowing you to examine and understand code structure during execution.
Purpose
Section titled “Purpose”The reflect module helps you:
- Analyze function signatures at runtime - Inspect parameters, names, and structure of functions without needing the source code
- Extract parameter information - Determine which parameters are required vs optional, their names, and positions
- Track code provenance - Keep track of where data and code originated from (file paths, line numbers, timestamps)
- Inspect TypeScript source content - Parse and analyze TypeScript code to extract exports, functions, and other declarations
Directory Structure
Section titled “Directory Structure”lib/reflect/├── callable.ts # Function analysis and signature inspection├── callable_test.ts # Tests for callable functionality├── provenance.ts # Source tracking and origin information├── provenance_test.ts├── reflect.ts # Core reflection utilities├── reflect_test.ts├── ts-content.ts # TypeScript source code analysis├── ts-content_test.ts├── value.ts # Runtime value inspection├── value_test.ts└── deps-test.ts # Dependency testsFile Reference
Section titled “File Reference”callable.ts
Section titled “callable.ts”Analyzes functions and callable objects to extract their signatures and parameter information.
Function Analysis
Section titled “Function Analysis”import { analyzeCallable } from "./callable.ts";
function greet(name: string, age?: number): string { return `Hello ${name}`;}
const info = analyzeCallable(greet);// Returns:// {// name: "greet",// params: [// { name: "name", required: true },// { name: "age", required: false }// ]// }This is particularly useful when you need to understand function signatures at runtime, such as when building tools that work with arbitrary functions.
Use Cases
Section titled “Use Cases”- Generating CLI help from functions - Automatically create command-line help text based on function parameters
- Validating function arguments - Check if the right arguments are being passed before calling a function
- Building documentation - Auto-generate API documentation from actual function definitions
provenance.ts
Section titled “provenance.ts”Tracks where code and data originated, providing an audit trail for debugging and error reporting.
Tracking Sources
Section titled “Tracking Sources”import { trackProvenance, getProvenance } from "./provenance.ts";
// Attach provenance information to dataconst data = trackProvenance({ value: 42 }, { source: "config.json", line: 10});
// Later, retrieve the origin informationconst origin = getProvenance(data);// Returns: { source: "config.json", line: 10 }This is especially valuable in complex applications where data flows through multiple transformations and you need to trace back to the original source.
Use Cases
Section titled “Use Cases”- Error messages with source locations - Show users exactly where in their config file an error occurred
- Debugging generated content - Track which template or generator created specific output
- Audit trails - Maintain records of where data came from for compliance or debugging
reflect.ts
Section titled “reflect.ts”Core reflection utilities for runtime type checking and object inspection.
import { getTypeOf, isObject, isFunction } from "./reflect.ts";
console.log(getTypeOf(42)); // "number"console.log(getTypeOf([])); // "array" (not just "object")console.log(isObject({})); // trueconsole.log(isFunction(() => {})); // trueThese utilities provide more accurate type detection than JavaScript’s built-in typeof operator, which has limitations (e.g., typeof [] returns "object" instead of "array").
ts-content.ts
Section titled “ts-content.ts”Analyzes TypeScript source code to extract structural information without executing it.
import { analyzeTypeScript } from "./ts-content.ts";
const source = `export function hello(name: string): string { return "Hello " + name;}`;
const analysis = analyzeTypeScript(source);// Returns:// {// exports: ["hello"],// functions: [{ name: "hello", exported: true }]// }This is useful for build tools, documentation generators, or any system that needs to understand TypeScript code structure without executing it.
value.ts
Section titled “value.ts”Provides deep runtime value inspection, going beyond simple type checking to analyze structure.
import { inspectValue, describeType } from "./value.ts";
const info = inspectValue({ a: 1, b: [2, 3] });// Returns detailed structure:// {// type: "object",// keys: ["a", "b"],// properties: {// a: { type: "number" },// b: { type: "array", length: 2 }// }// }
// More precise type descriptionsconsole.log(describeType(null)); // "null" (not "object")console.log(describeType(undefined)); // "undefined"console.log(describeType(42)); // "number"Usage Examples
Section titled “Usage Examples”Function Documentation Generator
Section titled “Function Documentation Generator”Automatically generate documentation for functions based on their signatures:
import { analyzeCallable } from "./callable.ts";
function buildTask( name: string, depends?: string[], options?: { timeout?: number }): void { // Task implementation...}
const info = analyzeCallable(buildTask);console.log(`Function: ${info.name}`);console.log("Parameters:");for (const param of info.params) { const req = param.required ? "(required)" : "(optional)"; console.log(` - ${param.name} ${req}`);}
// Output:// Function: buildTask// Parameters:// - name (required)// - depends (optional)// - options (optional)Source Tracking for Better Error Messages
Section titled “Source Tracking for Better Error Messages”Track configuration sources so you can provide helpful error messages:
import { trackProvenance, getProvenance } from "./provenance.ts";
// When loading config, attach source informationconst config = trackProvenance( JSON.parse(content), { source: filePath, timestamp: Date.now() });
// Later, when validation failsfunction validateConfig(cfg: unknown) { if (!isValidConfig(cfg)) { const source = getProvenance(cfg); throw new Error( `Invalid configuration in ${source.source}\n` + `Loaded at ${new Date(source.timestamp)}` ); }}Type Validation with Custom Assertions
Section titled “Type Validation with Custom Assertions”Create type guards with better error messages:
import { getTypeOf, isObject } from "./reflect.ts";
function validate(input: unknown): asserts input is Record<string, unknown> { if (!isObject(input)) { throw new Error( `Expected object, got ${getTypeOf(input)}. ` + `Value: ${JSON.stringify(input)}` ); }}
// Usagevalidate(someData);// Now TypeScript knows someData is an objectsomeData.property = "value"; // Type-safe!Integration Points
Section titled “Integration Points”With axiom/
Section titled “With axiom/”The reflect module integrates with the axiom parser to track source locations in error messages:
import { trackProvenance } from "../reflect/provenance.ts";
// During parsing, attach file location to AST nodesconst node = trackProvenance(parsed, { source: filePath, line: lineNum, column: colNum});
// Later, errors can reference the exact source locationWith task/
Section titled “With task/”Used for analyzing task functions to validate arguments and generate help:
import { analyzeCallable } from "../reflect/callable.ts";
// Analyze a task function to understand its parametersconst taskInfo = analyzeCallable(taskFn);
// Use this to validate task invocationsif (args.length < requiredParamCount) { throw new Error(`Task ${taskInfo.name} requires ${requiredParamCount} arguments`);}Design Notes
Section titled “Design Notes”Runtime vs Static Analysis
Section titled “Runtime vs Static Analysis”This module performs runtime analysis, which has both advantages and limitations:
Advantages:
- Works with actual values and functions as they exist during execution
- No need for TypeScript compiler API or complex AST parsing
- Can inspect values that were dynamically created
Limitations:
- Cannot access private TypeScript type information (generics, union types, etc.) at runtime
- Function parameter names may be minified in production builds
- Complex TypeScript features are erased during compilation
For static analysis (parsing source code without execution), use ts-content.ts, which can analyze TypeScript files as text.
Performance Considerations
Section titled “Performance Considerations”Reflection operations have some overhead:
- Function analysis requires iterating over parameters
- Provenance tracking adds metadata to objects
- Deep value inspection can be expensive for large objects
Use caching when analyzing the same functions repeatedly, and consider lazy evaluation for expensive operations.
Limitations
Section titled “Limitations”- No access to TypeScript type information - Types are erased at runtime, so you can’t inspect generic parameters or union types
- Minification affects parameter names - In production,
function greet(name)might becomefunction a(b) - Complex generics not captured - Runtime only sees JavaScript, not TypeScript’s type system
- Circular references - Deep value inspection may need special handling for circular object graphs
Testing
Section titled “Testing”Run the test suite to verify functionality:
# Test all reflect modulesdeno test lib/reflect/
# Test specific moduledeno test lib/reflect/callable_test.ts
# Run with coveragedeno test --coverage=coverage lib/reflect/Best Practices
Section titled “Best Practices”- Cache function analysis - If you analyze the same function multiple times, cache the results
- Use type guards - Combine with TypeScript’s type system for maximum safety
- Provide context in errors - Use provenance tracking to give users helpful error messages
- Document runtime limitations - Let users know when type information is unavailable
- Test with minified code - Ensure your reflection-based code works in production builds