TypeScript setup

esrun does not execute TypeScript, nor does it inject ambient globals like Bun or process. esdev runs TypeScript directly — types erased, never checked — and the definitions below are what your editor and tsc --noEmit use.

One-command setup

Shell
esdev --install-types

It adds @opentf/esrun-types as a dev dependency and adds it to compilerOptions.types — creating a tsconfig.json if you don't have one, or merging into an existing one without touching your other settings.

Which package manager it uses is decided in the order those answers are worth anything:

"packageManager" in package.jsonThe project saying which one it uses — the field corepack reads. It is there before a lockfile is: a fresh clone, a scaffold, a CI job at its first step
The lockfilebun.lock, bun.lockb, pnpm-lock.yaml, yarn.lock, package-lock.json — evidence of what actually installed
What is installed hereWith the project silent, the question is which manager can run at all

npm is the last word rather than the first: "npm is always there" is exactly the assumption that produces npm: command not found in a container that ships only bun. A declared manager that is not installed is still the answer — it prints the line to run rather than reaching for a different one, because installing with the wrong manager leaves the wrong lockfile behind.

What it configures

JSON
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "types": ["@opentf/esrun-types"]
  },
  "include": ["**/*.ts"]
}

types is what editors and language servers load globally, so the runtime:* modules resolve everywhere, not just per-file.

Leave `lib` unset

With no lib, TypeScript uses the default for your target — lib.esnext.full for ESNext — which already includes the web APIs this runtime provides: fetch, URL, crypto, the streams, TextEncoder, and Temporal from TypeScript 7. Setting lib explicitly replaces that default, so a config naming only ["ESNext"] loses all of them.

By hand
Shell
npm install --save-dev @opentf/esrun-types

Then add "types": ["@opentf/esrun-types"] to compilerOptions, or put /// <reference types="@opentf/esrun-types" /> in one file.

Use it

Your editor now provides intellisense, inline docs, and type checking for every built-in module:

TypeScript
import { file } from "runtime:fs";

// Your IDE knows `text()` returns a Promise<string>
const data = await file("./config.json").text();

Typechecking

esdev never checks types while it runs; your editor does, and so does:

Shell
esdev typecheck                  # tsc --noEmit, with the project's own TypeScript
esdev typecheck --pretty false   # arguments go to tsc untouched
esdev test --typecheck       # the tests, and the check, in one run

esdev typecheck runs the tsc the project installed, through its package manager (found as --install-types finds it), so the version that checks is the version the project uses. Its output is tsc's own.

Path aliases

compilerOptions.paths and baseUrl resolve the same way in esdev build, esdev test and esdev <file>. A JavaScript project states them in jsconfig.json, which is read when there is no tsconfig.json beside it. See Imports for the rules.

Class syntax ahead of the engine

accessor x = 1Compiled — into the private field and the getter/setter pair it is defined to be, on the prototype for an instance accessor and on the constructor for a static one. V8 has not shipped the keyword, so without this it would be a syntax error at load.
@decoratorRefused, by name, with what to write instead. Standard decorators are a stage-3 proposal the engine has not shipped and the compiler does not yet lower; TypeScript's older experimentalDecorators form is a different language feature and compiling one as the other would change what runs.

A web component that would use @customElement and @property says the same thing in plain JavaScript today — customElements.define(name, Class) and a static properties or observedAttributes — and both are read at definition time, the way a browser reads them.

Last updated on
Edit this page