Runtime#

There is no TypeScript runtime. The operator’s source is type-checked, then compiled (by tsc, esbuild, swc, vite, or built-in to tsx / deno / bun) to JavaScript, then executed on the JavaScript runtime (see Runtime). Types are erased during compilation; nothing TypeScript-specific exists at runtime.

Type erasure#

The compiler strips annotations, type declarations, interface declarations, as assertions, access modifiers, and generic type parameters. The output is plain JavaScript that runs on V8, JavaScriptCore, or SpiderMonkey.

interface User { id: string; name: string; }
const u: User = {id: "1", name: "rk"};
//         ^^^^ erased
// emits: const u = {id: "1", name: "rk"};

A few constructs do emit runtime code: enum (numeric and string), class with parameter properties (assignment to this.x), namespace (IIFE), and decorators (with experimentalDecorators or the standard decorator proposal).

tsc and tsconfig#

tsc is the reference compiler. tsconfig.json configures type-checking and code generation.

{
  "compilerOptions": {
    "target":            "ES2022",
    "module":            "NodeNext",
    "moduleResolution":  "NodeNext",
    "strict":            true,
    "noUncheckedIndexedAccess": true,
    "esModuleInterop":   true,
    "isolatedModules":   true,
    "skipLibCheck":      true,
    "outDir":            "dist",
    "rootDir":           "src",
    "declaration":       true,
    "sourceMap":         true
  },
  "include": ["src/**/*"]
}

The fields that matter most.

Option

Effect

strict

Enables every strict check (strictNullChecks, noImplicitAny, strictFunctionTypes, …). On for new code.

noUncheckedIndexedAccess

arr[i] is T | undefined instead of T. Catches a class of off-by-one bugs.

target

JS syntax level the output uses. ES2022 is the modern baseline.

module

Module syntax emitted. NodeNext for ESM in Node; ESNext for bundler-driven projects.

moduleResolution

How import "x" is resolved.

isolatedModules

Forces each file to be compilable independently. Required by single-file compilers (esbuild, swc).

declaration

Emit .d.ts files alongside .js. Set for shipped libraries.

skipLibCheck

Skip type-checking .d.ts files; required in practice to ship.

Run tsc --noEmit in CI for type-checking, and uses a faster compiler (esbuild / swc) for the JavaScript output.

Module systems#

TypeScript follows JavaScript: ESM is the modern default, CJS is the older alternative.

// ESM
export function greet(name: string): string { return `hello, ${name}`; }
import {greet} from "./greet.js";        // .js, even in source

For projects targeting Node ESM, write .js in import specifiers (TypeScript looks up ./greet.ts and emits ./greet.js). For bundler projects (Vite, esbuild), the extension is optional.

import type and export type are erased at compile time.

import type {Config} from "./config.js";          // erased
import {load}        from "./loader.js";          // runtime import

For projects with isolatedModules: true, type-only imports must be marked explicitly; tools (@typescript-eslint) auto-fix.

Declaration files (.d.ts)#

A .d.ts file declares types without code. Used to describe external JavaScript libraries (@types/* packages) and to ship type info for the operator’s own compiled libraries.

// env.d.ts
declare module "*.svg" {
  const content: string;
  export default content;
}

declare global {
  namespace NodeJS {
    interface ProcessEnv {
      readonly DATABASE_URL: string;
    }
  }
}
export {};

tsc --declaration emits .d.ts files for the project’s own modules; downstream consumers import them automatically when the package’s types field in package.json points at them.

package.json for libraries#

A typed library declares both runtime entry points and type entry points.

{
  "name": "myscan",
  "version": "0.1.0",
  "type": "module",
  "main":  "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types":  "./dist/index.d.ts"
    }
  },
  "scripts": {
    "build": "tsc -p tsconfig.build.json",
    "check": "tsc --noEmit"
  }
}

Running TypeScript directly#

tsx, ts-node, deno, and bun each strip types on-the-fly. Use tsx for Node-host scripts and deno / bun when they own the project.

$ npx tsx src/main.ts
$ deno run --allow-net src/main.ts
$ bun run src/main.ts

tsx does not type-check; the operator pairs it with tsc --noEmit in CI.

Runtimes that type-check on load#

Deno and Bun support running .ts directly and surface type errors as part of execution (configurable). For Node, the type-check is always a separate tsc pass.

JIT and codegen#

The same V8 / JavaScriptCore JIT runs the emitted code; the operator’s optimisation knobs are the JavaScript ones (see Runtime). TypeScript adds no runtime overhead.

References#