TypeScript AST Evaluator
ts-evaluator is an advanced interpreter for TypeScript that enables the evaluation of arbitrary AST (Abstract Syntax Tree) Nodes, specifically Expressions, ExpressionStatements, or Declarations, within a given TypeScript AST. Unlike tools such as `ts-node` that execute full TypeScript programs, this library focuses on partial evaluation based on a node's lexical environment. The current stable version is 2.0.0. Release cadence appears to be driven by significant TypeScript and JSDOM version updates, typically with several minor and patch releases in between. Its key differentiators include the ability to evaluate specific nodes, support for browser, Node.js, and pure ECMAScript environments, and configurable policy options for sandboxing and restricting operations like I/O or network access. This makes it a valuable tool for linters, language services, partial evaluators, and frameworks requiring deep AST introspection and computation.
Common errors
-
Error: Evaluation failed: Cannot evaluate node '...' because the Host cannot resolve its' ModuleSpecifier '...'
cause The evaluator failed to resolve an imported module, likely due to a missing or misconfigured `ModuleOverrides` option in the evaluation context.fixProvide `ModuleOverrides` in `createEvaluationContext` to map module specifiers to their resolved values or mock implementations. Example: `createEvaluationContext({ moduleOverrides: new Map([['my-module', { foo: 123 }]]) })`. -
TypeError: Cannot read properties of undefined (reading 'kind')
cause This often happens when `evaluate` is called with a `ts.Node` that is `undefined` or `null`, meaning the AST node was not correctly found or provided.fixEnsure the `ts.Node` passed to `evaluate()` is a valid and existing node from the parsed `SourceFile`. Add null/undefined checks before calling `evaluate`. -
SyntaxError: Cannot use import statement outside a module
cause Running `ts-evaluator` (or code using it) in a CommonJS environment without proper configuration or when trying to `require` the ESM-first package.fixEnsure your project is configured for ES Modules (`"type": "module"` in `package.json`, `.mjs` files), or that your bundler correctly handles ESM to CJS transpilation. Prefer `import` syntax. -
TypeError: Node.js version 16 or newer is required to run this package. You are currently running vXX.YY.Z.
cause Using an outdated Node.js version that does not meet the minimum requirement for `ts-evaluator` (v18.20.0 for v2.0.0, v14.19.0 for v1.x).fixUpdate your Node.js environment to the version specified in the `engines` field of the `ts-evaluator` package. Use `nvm` or your preferred Node.js version manager.
Warnings
- breaking Version 2.0.0 introduces a breaking change, requiring Node.js v18.20.0 or newer. Ensure your environment meets this minimum requirement before upgrading.
- breaking Starting from v1.0.1, `ts-evaluator` transitioned to an ESM-first package. While it provides a CommonJS fallback, applications primarily using CommonJS might need to adjust import statements or package configurations.
- gotcha `ts-evaluator` is a peer dependency on `typescript` and `jsdom`. While this allows flexibility, ensure you have compatible versions of these packages installed in your project, as their APIs are heavily relied upon.
- gotcha Evaluating certain complex expressions or nodes without a `typeChecker` can lead to less robust or incorrect evaluations. While `typeChecker` is optional, its absence can limit the evaluator's accuracy.
- gotcha `ts-evaluator` is designed for evaluating individual AST nodes, not for running entire TypeScript programs or scripts like a REPL. Attempting to pass full programs or statement sequences for evaluation will not yield the expected results.
Install
-
npm install ts-evaluator -
yarn add ts-evaluator -
pnpm add ts-evaluator
Imports
- evaluate
const { evaluate } = require('ts-evaluator')import { evaluate } from 'ts-evaluator' - createEvaluationContext
import { createEvaluationContext } from 'ts-evaluator' - createSourceFile
import { createSourceFile, ScriptTarget, SyntaxKind } from 'typescript'
Quickstart
import { evaluate, createEvaluationContext } from 'ts-evaluator';
import ts, { createSourceFile, ScriptTarget, SyntaxKind } from 'typescript';
const code = `
const a = 10;
const b = 'hello';
function greet(name: string) { return 'Hello, ' + name + '!'; }
const result = greet(b);
const obj = { x: a, y: result };
obj.x + 5;
`;
// Create a SourceFile from the code
const sourceFile = createSourceFile('example.ts', code, ScriptTarget.ES2016, true);
// Find the node to evaluate, e.g., 'obj.x + 5'
const nodeToEvaluate = sourceFile.forEachChild(node => {
if (ts.isBinaryExpression(node) && ts.isPropertyAccessExpression(node.left) && node.left.name.text === 'x') {
return node;
}
return undefined;
});
if (!nodeToEvaluate) {
console.error('Could not find the node to evaluate.');
} else {
// Create an evaluation context, optionally passing a TypeScript TypeChecker and policy options
const context = createEvaluationContext({
typeChecker: ts.createProgram([sourceFile.fileName], { target: ScriptTarget.ES2016 }).getTypeChecker(),
// Example policy: disallow I/O operations
policy: {
disableIO: true,
disableNetwork: true
},
// Initial lexical environment variables (if any)
lexicalEnvironment: new Map()
});
// Evaluate the node
const evaluationResult = evaluate(nodeToEvaluate, context);
if (evaluationResult.success) {
console.log(`Evaluation successful! Value: ${evaluationResult.value}`);
// Expected output: Evaluation successful! Value: 15
} else {
console.error(`Evaluation failed: ${evaluationResult.reason}`);
}
}