TypeScript with Bun: Native Execution Without a Compile Step
AI generated
type
TypeScript · Bun · Runtime
TypeScript with Bun
Native execution without a separate compile step, but with clear limits

Bun runs TypeScript files directly, with no need for tsc or a bundler to run first, which noticeably shortens development loops. What matters to understand is that Bun only transpiles TypeScript, stripping types and translating syntax, but performs no type checking, that responsibility still sits with a separate tsc invocation or the IDE.

9 min read Bun Runtime Tooling

1. How Bun runs TypeScript without a separate build

Bun is built on the JavaScriptCore engine and ships a built-in TypeScript transpiler that strips type annotations and translates modern syntax features to JavaScript directly when a .ts file is loaded, before the engine executes the code. This step happens internally, without spawning a separate tsc process or producing an output directory with compiled files.

The crucial difference from a classic ts-node or tsx setup under Node.js is that transpilation is natively built into Bun instead of running through an additional npm package, which noticeably speeds up startup. Bun also caches transpiled modules internally, so repeated runs of the same script are not re-transpiled every time.


# Run main.ts directly, no prior build step needed
bun run main.ts

# Works identically through npm scripts too
bun run start   # package.json: "start": "bun main.ts"

2. Transpilation is not a typecheck: the most important caveat

The most common misconception about Bun is assuming that a successfully running .ts file is automatically type-correct. Bun strips type annotations purely syntactically, it does not check whether a function is called with the wrong argument types or whether a property is accessed on a possibly undefined value, such errors surface as a runtime crash instead of an early compile error.

For real type safety, a separate tsc --noEmit run remains necessary, whether locally in the IDE, as a pre-commit hook, or as its own CI step. That run produces no output, it serves purely as a type check, while Bun continues to handle the actual execution.


# Bun happily runs this file even though the type is wrong
# add(a: number, b: number) is called with a string
bun run buggy.ts   # runs, may only crash on incorrect usage

# Surface type errors only with a separate tsc check
npx tsc --noEmit

3. How Bun handles tsconfig.json and compiler options

Bun reads a project's tsconfig.json for certain settings, in particular paths for module aliases, baseUrl, and jsx-related options that affect the runtime behavior of the transpilation. Pure type-checking options like strict or noImplicitAny, on the other hand, have no effect on execution at all, because Bun simply does not evaluate those rules.

This creates an important separation: tsconfig.json remains the configuration source for a separate typecheck run with tsc, while Bun only pulls out the parts needed for correct module resolution and syntax transformation.


{
  "compilerOptions": {
    "strict": true,
    "paths": { "@app/*": ["src/*"] },
    "baseUrl": "."
  }
}
// Bun honors the paths aliases at runtime,
// "strict" only affects a separate tsc run

4. Testing directly in TypeScript with bun test

The built-in test runner bun test runs TypeScript test files with no build step and ships a Jest-compatible API, which in many cases lets existing Jest test suites migrate without major rewrites. Execution speed is typically noticeably higher than a Jest setup with ts-jest, thanks to the built-in transpilation and the fast engine.

What still matters here: bun test does not check types, a test with an incorrectly typed assertion still passes as long as the runtime logic is correct. A separate typecheck step in the CI pipeline therefore remains indispensable even when using bun test.


import { describe, it, expect } from "bun:test";
import { add } from "./math";

describe("add", () => {
  it("adds two numbers correctly", () => {
    expect(add(2, 3)).toBe(5);
  });
});

5. Node compatibility and typical migration pitfalls

Bun implements a large part of the Node.js APIs, including fs, path, and large portions of node:http, which makes many existing Node TypeScript projects run without major changes. Not every Node API is implemented completely or identically, though, differences can show up especially in rarer APIs from worker_threads or specific stream behaviors.

When migrating an existing Node project, it is worth first running the test suite under Bun to catch compatibility gaps early, rather than blindly relying on complete Node equivalence. Bun continuously updates its Node compatibility, so checking the current compatibility overview before a migration is a good idea.

6. Watch mode and hot reload for faster development loops

With the --watch flag, Bun monitors every file a script imports and automatically restarts the process as soon as one of them changes, with no need to install an extra tool like nodemon. Because transpilation is natively built in anyway, the restart is typically noticeably faster than comparable Node-based watch setups that additionally have to run through an external transpilation package.

For HTTP servers, Bun additionally offers real hot reloading via --hot, where existing connections stay alive and only the changed module code gets swapped out, instead of restarting the entire process. That shrinks downtime during development to practically zero and makes iterating on server code noticeably more pleasant.


# Automatic restart on file change
bun --watch run src/index.ts

# Hot reload for an HTTP server without dropping connections
bun --hot run src/server.ts

7. Production builds are still worthwhile: bun build

Even though Bun can run TypeScript directly at runtime, an explicit build step with bun build is usually still worthwhile for production deployments, one that bundles the code, minifies it, and avoids the internal transpilation at runtime. A pre-built artifact starts faster and reduces the attack surface, because the source code does not sit in plain text on the production system.

For local development and quick scripts, on the other hand, direct execution without a build is the biggest productivity win, because every code change can be tested immediately without any wait, shrinking the classic edit-compile-run cycle down to a plain edit-run cycle.


# Development: direct execution without a build
bun run src/index.ts

# Production: explicit bundling and minification
bun build src/index.ts --outdir dist --minify --target bun

8. IDE integration and the typecheck in the editor

While Bun does not check types at runtime, the IDE independently keeps working with the TypeScript language server, which shows type errors live in the editor regardless of whether the script is later run with Bun or Node. This separation means developers in practice still get immediate feedback on type errors, just not from the runtime itself but from the IDE.

For teams that need this guarantee outside the IDE too, for example developers with different editor setups, an explicit tsc --noEmit step in CI remains the only reliable safeguard, regardless of which local environment a given developer uses.

9. When Bun pays off for TypeScript projects

For quick scripts, local tools, and development servers, native TypeScript execution without a compile step is a noticeable productivity gain, because the edit-run cycle becomes practically instant. For production deployments, an explicit build with bun build is usually still worthwhile, and a separate typecheck step is indispensable in any case, regardless of whether Bun or Node serves as the runtime.

The biggest mistake when switching is confusing runnability with type correctness, Bun only guarantees the former. Anyone who keeps this separation clearly in mind from the start and keeps tsc as an independent CI step benefits from the speed without losing the safety of type checking.

Feature Bun (bun run) ts-node/tsx under Node tsc build plus node
Startup time very fast, natively built in medium, extra package slow on first build
Type checking included no, transpilation only no, transpilation only yes, complete
Node API compatibility large, but not complete complete, it is Node complete, it is Node
Built-in test runner yes, bun test no, external tool needed no, external tool needed
Production build optional, bun build usually its own build step always, tsc itself

Mironsoft

TypeScript migration, type safety, and team onboarding

A JavaScript codebase without type safety, but no time for a full migration?

We migrate existing JavaScript projects to TypeScript step by step, set up strict compiler settings cleanly, and bring teams to the same type-safety level with code reviews and style guides.

Migration Roadmap

Plan and execute a gradual JS-to-TS migration without big-bang risk.

Strict Mode Rollout

Set up tsconfig.json, ESLint rules, and CI checks for lasting type safety.

Team Onboarding

Bring developers up to speed on TypeScript best practices with workshops and reviews.

10. Summary

TypeScript with Bun

Core idea

Bun transpiles TypeScript internally and runs it directly

Key limitation

No type checking at runtime, tsc remains necessary

Testing

bun test with a Jest-compatible API, also without typecheck

Production

bun build for bundled, faster-starting artifacts

11. FAQ: TypeScript with Bun

1Does Bun check types when running TypeScript?
No, Bun only strips type annotations syntactically and runs the resulting JavaScript code, no real type checking happens, a separate tsc run is still needed for that.
2Can I use strict mode in tsconfig.json to make Bun stricter?
No, compiler options like strict or noImplicitAny only affect a separate tsc run, Bun does not evaluate those options for execution.
3Do I still need a build step for production?
Usually yes, bun build produces bundled, minified artifacts that start faster and do not leave the source code in plain text on the production system.
4Do existing Node TypeScript projects work under Bun automatically?
In many cases yes, because Bun implements a large part of the Node APIs, but there are gaps in rarer APIs, running the test suite under Bun surfaces compatibility issues early.
5Is bun test a full replacement for Jest?
For most standard cases yes, thanks to the Jest-compatible API, but with very specific Jest plugins or custom matchers there can be gaps that require individual adjustments.
6How fast is bun run compared to ts-node?
Considerably faster, because transpilation is natively built into Bun instead of running through an additional npm package, the difference is especially noticeable at startup.
7Does the IDE still show me type errors when running with Bun?
Yes, the IDE's TypeScript language server works independently of the runtime and shows type errors live in the editor, regardless of whether the script is later run with Bun or Node.
8Does Bun respect path aliases from tsconfig.json?
Yes, paths and baseUrl are honored by Bun for module resolution, unlike pure type-checking options, which have no effect on execution.
9Should I still run tsc in CI if I use Bun?
Yes, absolutely, a separate tsc --noEmit step remains the only reliable safeguard against type errors, regardless of the runtime used.
10Is Bun suited for large production backends?
Increasingly yes, many teams run Bun in production, but it matters to check Node API compatibility for the specific modules used beforehand and to plan for an explicit build step.