Quick start · project shape

Type-checked JavaScript for Bun

Bun runs JavaScript and TypeScript files directly, and per the Bun documentation it runs them without checking their types. Checking is a separate step: tsc -p jsconfig.json with Bun's types installed, or Bun's own bun check (below).

  • Bun
  • 2 dev dependencies
  • npm run lint

The config follows the compiler options Bun's documentation recommends, adapted for .js files: checkJs instead of allowJs, and none of the options that only matter for .ts files. The files below are one project, checked with TypeScript 5.9 when this site is built.

Install

@types/bun declares the Bun global and Bun's modules; per the Bun documentation, install it with bun add -d @types/bun. TypeScript provides tsc. Install both as development dependencies (with npm, npm install --save-dev typescript @types/bun):

Terminal
bun add -d typescript @types/bun

jsconfig.json

module: "preserve" with moduleResolution: "bundler" matches how Bun resolves imports, lib: ["esnext"] with types: ["bun"] gives Bun's APIs and no DOM, and moduleDetection: "force" treats every file as a module, as Bun does. Bun's recommended config also turns on noUncheckedIndexedAccess, noImplicitOverride and noFallthroughCasesInSwitch; add them if you want the stricter level.

jsconfig.json
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "module": "preserve",
    "moduleResolution": "bundler",
    "target": "esnext",
    "lib": ["esnext"],
    "moduleDetection": "force",
    "types": ["bun"],
    "skipLibCheck": true
  },
  "include": ["src/**/*.js"]
}

npm scripts

Run them with bun run lint or npm run lint; either way it is tsc that checks.

package.json
{
  "type": "module",
  "scripts": {
    "lint": "tsc -p jsconfig.json",
    "lint:watch": "tsc -p jsconfig.json --watch --preserveWatchOutput"
  }
}

bun check

The Bun documentation (Bun 1.4) describes a built-in checker, bun check, that reads tsconfig.json, writes no files, and aims to report the same errors as tsc from TypeScript 7. It reports errors in JavaScript files when checkJs is on. That documentation talks about tsconfig.json and does not mention jsconfig.json, so with bun check name the file tsconfig.json. We have not run it; the example on this page is checked with tsc 5.9.

Per the same documentation, bun --check file.js checks before it runs, and bun test --check checks before testing.

What it catches

Bun's APIs are typed like any other: a misspelled method on Bun.file() is an error.

src/server.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: src/server.js(9,55): error TS2551: Property 'txt' does not exist on type 'BunFile'. Did you mean 'text'? 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. src/server.js(9,55): error TS2551: Property 'txt' does not exist on type 'BunFile'. Did you mean 'text'? 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. src/server.js(9,55): error TS2551: Property 'txt' does not exist on type 'BunFile'. Did you mean 'text'? checked 21 October 2026
const port = Number(Bun.env.PORT ?? 3000);

const server = Bun.serve({
  port,
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/notes") {
      return new Response(await Bun.file("notes.txt").txt());error TS2551: Property 'txt' does not exist on type 'BunFile'. Did you mean 'text'?
    }
    return Response.json({ path: url.pathname });
  },
});

console.log(`Listening on ${server.url}`);
A misspelled Bun API.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "module": "preserve",
    "moduleResolution": "bundler",
    "target": "esnext",
    "lib": [
      "esnext"
    ],
    "moduleDetection": "force",
    "types": [
      "bun"
    ],
    "skipLibCheck": true
  },
  "include": [
    "src/**/*.js"
  ]
}

The example project

A small Bun.serve server that reads a port from Bun.env and a file with Bun.file.

src/server.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
const port = Number(Bun.env.PORT ?? 3000);

const server = Bun.serve({
  port,
  async fetch(request) {
    const url = new URL(request.url);
    if (url.pathname === "/notes") {
      return new Response(await Bun.file("notes.txt").text());
    }
    return Response.json({ path: url.pathname });
  },
});

console.log(`Listening on ${server.url}`);
A Bun server, checked with tsc.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "module": "preserve",
    "moduleResolution": "bundler",
    "target": "esnext",
    "lib": [
      "esnext"
    ],
    "moduleDetection": "force",
    "types": [
      "bun"
    ],
    "skipLibCheck": true
  },
  "include": [
    "src/**/*.js"
  ]
}

In your editor

VS Code reads this jsconfig.json with no further setup. To make it use the same TypeScript as npm run lint, see VS Code.

Sources

Last reviewed 20 October 2026.