Gathering detailed insights and metrics for ts-evaluator
Gathering detailed insights and metrics for ts-evaluator
Gathering detailed insights and metrics for ts-evaluator
Gathering detailed insights and metrics for ts-evaluator
@wessberg/ts-evaluator
An interpreter for Typescript that can evaluate an arbitrary Node within a Typescript AST
ts-expression-evaluator
Context-based expression parse and evaluator.
ts-transforms
An ETL framework built upon xlucene-evaluator
ts-interpreter.js
This package will allow you to use `require` on any `.ts` files from any `.js` files using our runtime compiler!
An interpreter for Typescript that can evaluate an arbitrary Node within a Typescript AST
npm install ts-evaluator
Typescript
Module System
Min. Node Version
Node Version
NPM Version
84.6
Supply Chain
98.5
Quality
76.3
Maintenance
100
Vulnerability
98.6
License
TypeScript (99.63%)
JavaScript (0.34%)
Shell (0.03%)
Total Downloads
5,552,844
Last Day
3,473
Last Week
106,625
Last Month
429,672
Last Year
4,128,688
MIT License
98 Stars
180 Commits
1 Forks
3 Watchers
1 Branches
2 Contributors
Updated on Jun 12, 2025
Minified
Minified + Gzipped
Latest Version
2.0.0
Package Id
ts-evaluator@2.0.0
Unpacked Size
1.07 MB
Size
169.36 kB
File Count
9
NPM Version
10.8.3
Node Version
22.9.0
Published on
Oct 10, 2024
Cumulative downloads
Total Downloads
Last Day
-4.7%
3,473
Compared to previous day
Last Week
0.1%
106,625
Compared to previous week
Last Month
-4%
429,672
Compared to previous month
Last Year
205.9%
4,128,688
Compared to previous year
3
2
51
An interpreter for Typescript that can evaluate an arbitrary Node within a Typescript AST
This library is an implementation of an interpreter for Typescript that can evaluate any Expression
, ExpressionStatement
or Declaration
within a Typescript AST.
Rather than interpreting a program, or a sequence of Statement
s, this library takes a Node within an existing AST and evaluates it based on its' lexical environment.
This makes the library an effective companion if you're building a linter, framework, language service, partial evaluator, or something else where you may want to know the computed value of a specific Node at any point in an AST.
One strength of this plugin is that it opens up entirely new use cases such as partial evaluation directly in the editor experience, for example to catch non-syntactic bugs that would only occur on runtime, or more advanced diagnostic for frameworks.
To that end, several policy options can be provided to configure restrictions in terms of what is allowed to be evaluated, such as IO and Network access.
Additionally, ts-evaluator
supports both a Browser environment, a Node environment, and a pure ECMAScript environment. See Setting up an environment for more details.
If you are looking for a Typescript REPL, or a way to execute a full Typescript program, you're looking for something like ts-node instead.
$ npm install ts-evaluator
$ yarn add ts-evaluator
$ pnpm add ts-evaluator
ts-evaluator
depends on typescript
, so you need to manually install this as well.
You may also need to install jsdom
depending on the features you are going to use. Refer to the documentation for the specific cases where it may be relevant.
Let's start off with a very basic example:
1import {evaluate} from "ts-evaluator"; 2 3const result = evaluate({ 4 node: someNode, 5 typeChecker: someTypeChecker 6}); 7 8// If a value was produced 9if (result.success) { 10 console.log(result.value); 11} 12 13// If an error occurred 14else { 15 console.log(result.reason); 16}
In this example, the referenced bindings within the lexical environment of the Node will be discovered and evaluated before producing a final value. This means that you don't have to evaluate the entire program to produce a value which may potentially be a much faster operation.
If you do not have access to a typechecker, for example if you don't have a TypeScript Program to work with, you can avoid passing in a typechecker as an option.
This won't be as robust as when a typechecker is given, as ts-evaluator
won't understand the full type hierarchy of your Program, and most importantly not understand
how to resolve and dealias symbols and identifiers across source files, but it will still be able to resolve and evaluate identifiers and symbols that are located in the same
SourceFile. Uou may find that it works perfectly well for your use case.
You can define the kind of environment that evaluate()
assumes when evaluating the given Node. By default, a CommonJS-based Node
environment is assumed, to align with what you would get simply by running node
with no arguments.
The following environment presets are supported:
ECMA
- Assumes a pure ECMAScript environment. This means that no other globals than those that are defined in the ECMAScript spec such as Math
, Promise
, Object
, etc, are available.NODE
(default) - Assumes a CommonJS-based Node.js environment. This means that built-in modules such as fs
and path
can be resolved, and Node-specific globals such as process
is present, as well as ones that are only present in a CommonJS-based Node.js environment, such as require
, __dirname
, and __filename
.NODE_CJS
- An alias for NODE
.NODE_ESM
- Assumes an ESM-based Node.js environment. This means that built-in modules such as fs
and path
can be resolved, and Node-specific globals such as process
is present, as well as ones that are only present in an ESM-based Node.js environment, such as import.meta
.BROWSER
- Assumes a Browser environment. This means that DOM APIs are available and Browser-specific globals such as window
is present.Beyond presets, you can provide additional globals or override those that comes from the presets. Here's how you can configure environment options:
1const result = evaluate({ 2 // ... 3 environment: { 4 // The "Node" environment is the default one. You can simply omit this key if you are targeting a CommonJS-based Node environment 5 preset: "NODE", 6 extra: { 7 someGlobal: "someValue" 8 } 9 } 10});
With great power comes great responsibility. If you are embedding this plugin in, say, a language service plugin to enhance the editing experience in an editor, you may want to apply some restrictions as to what can be evaluated.
By default, IO writes, network calls, and spawning child processes are restricted. You can customize this to your liking:
1const result = evaluate({ 2 // ... 3 policy: { 4 deterministic: false, 5 network: false, 6 console: false, 7 maxOps: Infinity, 8 maxOpDuration: Infinity, 9 io: { 10 read: true, 11 write: false 12 }, 13 process: { 14 exit: false, 15 spawnChild: false 16 } 17 } 18});
Here's an explainer of the individual policies:
deterministic
(default: false
) - If deterministic
is true
, only code constructs that always evaluate to the same value is permitted. This means that things like Math.random()
or new Date()
without arguments, as well as network calls are restricted.
This is useful if you are trying to statically analyze something and need to make sure that the value won't change for each invocation.
network
(default: false
) - If network
is true
, network activity is allowed, such as sending an HTTP request or hooking up a server.
console
(default: false
) - If console
is true
, logging to the console within evaluated code will produce the side-effect of actually logging to the console of the parent process. Usually, this is unwanted, since you're most likely only interested in the
evaluated value, not so much the side-effects, but you can override this behavior by setting console
to true
.
maxOps
(default: Infinity
) - If maxOps
is anything less than Infinity, evaluation will stop when the provided amount of operations has been performed. This is useful to opt-out of running CPU-intensive code, especially if you are embedding this library in an editor or a linter.
maxOpDuration
(default: Infinity
) - If maxOpDuration
is anything less than Infinity, evaluation will stop when the provided amount of milliseconds have passed. This is useful to opt-out of long-running operations, especially if you are embedding this library in an editor or a linter.
io
(default: {read: true, write: false}
) - If io
permits READ
operations, files can be read from disk. If io
permits WRITE
operations, files can be written to disk.
process
(default: {exit: false, spawnChild: false}
) - If process
permits exit
operations, the evaluated code is permitted to exit the parent process. If process
permits spawnChild
operations, the evaluated code is permitted to spawn child processes.
You can provide a specific version of TypeScript to use as an option to evaluate
. This may come in handy if you're using
multiple TypeScript versions in your project or if you're receiving the TypeScript version to use as an argument from a third party.
1const result = evaluate({ 2 // ... 3 typescript: someTypescriptModule 4});
You can get information about the evaluation process with various levels of logging. By default, nothing is logged, but you can override this behavior:
1const result = evaluate({ 2 // ... 3 logLevel: LogLevelKind.DEBUG 4});
Here's an explainer of the different log levels:
LogLevelKind.SILENT
(default) - By default, nothing is logged to the console.LogLevelKind.INFO
- Intermediate results are logged to the console.LogLevelKind.VERBOSE
- Everything that is logged with LogLevelKind.INFO
as well as lexical environment bindings are logged to the consoleLogLevelKind.DEBUG
- Everything that is logged with LogLevelKind.VERBOSE
as well as all visited Nodes during evaluation are logged to the consoleYou can tap into the evaluation process with reporting hooks that will be invoked with useful information while an evaluation is in progress. These are useful if you want to understand more about the execution path and work with it programmatically.
1const result = evaluate({ 2 // ... 3 reporting: { 4 reportBindings: entry => doSomething(entry), 5 reportTraversal: entry => someArray.push(entry.node), 6 reportIntermediateResults: entry => doSomeOtherThing(entry), 7 reportErrors: entry => doSomethingWithError(entry) 8 } 9});
Here's an explainer of the different reporting hooks:
reportBindings(entry: IBindingReportEntry) => void|(Promise<void>)
- Will be invoked for each time a value is bound to the lexical environment of a Node. This is useful to track mutations throughout code execution, for example to understand when and where variables are declared and/or mutated.reportTraversal(entry: ITraversalReportEntry) => void|(Promise<void>)
- Will be invoked for each time a new Node is visited while evaluating. This is useful to track the path through the AST, for example to compute code coverage.reportIntermediateResults(entry: IIntermediateResultReportEntry) => void|(Promise<void>)
- Will be invoked for each intermediate result that has been evaluated before producing a final result. This allows you to work programmatically with all expression values during code execution.reportErrors(entry: IErrorReportEntry) => void|(Promise<void>)
- Will be invoked for each error that is thrown, both when evaluating a result, and for subsequent invocations on, for example, returned function instances. Holds a reference to the error, as well ast the AST node that threw or caused the Error.Sometimes, evaluating identifiers require resolving identifiers across source files. When a typechecker is passed in, so long as the identifier is resolvable as part of the compilation unit,
this won't ever be an issue. However, when a type checker is not passed in, or in case you want to override the result of requiring an external module, you can use the moduleOverrides
option.
For example, you may want to pass in as shim for a built-in module. To use it, pass a record where the keys are module specifiers and the value is what requiring that module should resolve to. For example:
1{ 2 fs: { 3 readFileSync: () => {}, 4 // ... 5 } 6}
Do you want to contribute? Awesome! Please follow these recommendations.
Frederik Wessberg Twitter: @FredWessberg Github: @wessberg Lead Developer |
This is, after all, a virtual machine written on top of another virtual machine (V8), which is built in a dynamically typed high-level language (EcmaScript). This library is not built to be
comparable in performance to raw V8 execution speed. However, since ts-evaluator
doesn't require a compile-step and works directly on an AST, for small operations it will most likely be several magnitudes faster than
both ts-node
and compiling to JavaScript with tsc
and executing directly.
MIT © Frederik Wessberg (@FredWessberg) (Website)
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
7 existing vulnerabilities detected
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 1/28 approved changesets -- score normalized to 0
Reason
security policy file not detected
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-06-23
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More