August 7, 2026

Add Type Checking and TSConfig to Bun-Create-Playwright

Now that we have installed bun, Anthropic's package manager, and scaffolded a Playwright framework and closely examined the results, we are going to explore with our Bun-Create-Playwright project ways to check if our code is correct. 

The first method we will be exploring is typechecking

Why Typechecking? As the Playwright.dev / Node.js Introduction mentions:
"[...] Playwright does not check the types and will run tests even if there are non-critical TypeScript compilation errors. We recommend you run TypeScript compiler alongside Playwright.

"[...] Note that Playwright only supports the following tsconfig options: allowJs, baseUrl, paths, references and extends.

"[...] By default, Playwright will look up a closest tsconfig for each imported file by going up the directory structure and looking for tsconfig.json or jsconfig.json. This way, you can create a tests/tsconfig.json file that will be used only for your tests and Playwright will pick it up automatically".
Before we go further down this road...

What is Type Checking? 


According to Type Checking in TypeScript: A Beginners Guide, every piece of data in TypeScript is given a "type", and this "type" determines what properties the data has, and what methods are available to it. The types can be things like a Number, String, Enum, Boolean, Array, Object, Type assertions, or others.

Since all data in TypeScript is given a type, it means that the TypeScript compiler, tsc, can check every property to make sure it is being used correctly. It makes sure that the expected properties exist and that the methods and functions are all compatible. 

And best of all, you can run these checks in your IDE as you are writing the code! 

To run typechecking against our code, all we have to do is execute from the Terminal:
  • tsc --noEmit
Since we are just kicking the tires of our code to see what happens, we don't need to emit "compiler output files like JavaScript source code, source-maps or declarations. This makes room for another tool like Babel, or swc to handle converting the TypeScript file to a file which can run inside a JavaScript environment. You can then use TypeScript as a tool for providing editor integration, and as a source code type-checker". ( TypeScriptLang.org / noEmit )

The only problem? We need a TypeScript configuration file added to our project before we do that, and as you could see when we examined what "bun create playwright" produced, we don't have one. 

If you try to run the "tsc --noEmit" command without a tsconfig.json, you get the message: 
Version 6.0.3
tsc: The TypeScript Compiler - Version 6.0.3                                                                        
                                                                                                                 
TS COMMON COMMANDS

  tsc: Compiles the current project (tsconfig.json in the working directory.)

  tsc app.ts util.ts: Ignoring tsconfig.json, compiles the specified files with default compiler options.

  tsc -b: Build a composite project in the working directory.

  tsc --init: Creates a tsconfig.json with the recommended settings in the working directory.

  tsc -p ./path/to/tsconfig.json: Compiles the TypeScript project located at the specified path.

  tsc --help --all: An expanded version of this information, showing all possible compiler options

  tsc --noEmit
  tsc --target esnext
  Compiles the current project, with additional settings.

COMMAND LINE FLAGS

      --help, -h  Print this message.
    ... 
              --outFile  Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true,                          also designates a file that bundles all .d.ts output.

               --outDir  Specify an output folder for all emitted files.

You can learn about all of the compiler options at https://aka.ms/tsc
So, how do we create with bun a tsconfig.json file? 
  • bun init

What does Bun Init Do?

According to the Bun.com / Init Templating docs, It creates: 
  • a package.json file with a name that defaults to the current directory name
  • a tsconfig.json or jsconfig.json file, depending on whether the entry point is a TypeScript file
  • an entry point, which defaults to index.ts unless any of index.{tsx, jsx, js, mts, mjs} exist or the package.json specifies a module or main field
  • a README.md file
It also creates AI Agent rules (disable with $BUN_AGENT_RULE_DISABLED=1):
  • a CLAUDE.md file when Claude CLI is detected (disable with CLAUDE_CODE_AGENT_RULE_DISABLED env var)
  • a .cursor/rules/*.mdc file when Cursor is detected, which tells Cursor AI to use Bun instead of Node.js and npm
But we already scaffolded a Playwright project! Do we have to start from scratch and do everything all over again? 

No, but we do need to make some modifications:
  • Does your package.json in your root folder of the project have "main" to be "index.js"? Change that JavaScript extension to ".ts".
  • Do you have a "jsconfig.json" file? Delete it. 
  • Do you have an index.html file? Delete it. 
  • Then run: bun init 

TSConfig.json Achieved! First version!

The resulting tsconfig.json file produced by "bun init", according to Google AI, "configures TypeScript to act strictly as a type-checker while offloading the actual bundling, compiling, and running of your JavaScript/TypeScript code to Bun". 
{
  "compilerOptions": {
    // Environment setup & latest features
    "lib": ["ESNext"],
    "target": "ESNext",
    "module": "Preserve",
    "moduleDetection": "force",
    "jsx": "react-jsx",
    "allowJs": true,
    "types": ["bun"],

    // Bundler mode
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,

    // Best practices
    "strict": true,
    "skipLibCheck": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,

    // Some stricter flags (disabled by default)
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noPropertyAccessFromIndexSignature": false
  }
}

Environment setup & latest features

These settings ensure TypeScript understands the latest syntax and runtime environment variables.

  • "lib": ["ESNext"] -- includes type definitions for the newest JavaScript standard library features (Promise, Array methods, etc.).
  • "target": "ESNext" -- compiles/type-checks against the latest ECMAScript syntax rather than downleveling to an older version.
  • "module": "Preserve" -- keeps your import/export statements as written (doesn't rewrite ESM to CommonJS or vice versa); left for Bun to handle at runtime.
  • "moduleDetection": "force" -- treats every file as a module (adds import/export scoping) even if it has no import/export statements itself.
  • "jsx": "react-jsx" -- compiles JSX using the React 17+ automatic runtime (no need to import React in every file that uses JSX).
  • "allowJs": true --  lets .js files be included and type-checked alongside .ts files.
  • "types": ["bun"] -- only loads Bun's global type definitions (e.g. Bun.serve), instead of auto-including every @types/* package found in node_modules.

Bundler mode

These rules match how modern build tools resolve file paths and handle module definitions.

  • "moduleResolution": "bundler" -- resolves imports the way modern bundlers do, closer to Node's ESM resolution but more permissive (works with paths, package exports, etc.).
  • "allowImportingTsExtensions": true -- permits writing import './foo.ts' with the explicit extension (normally disallowed).
  • "verbatimModuleSyntax": true -- requires type-only imports to be explicitly marked (import type { Foo }), so the compiler doesn't have to guess what to strip at build time.
  • "noEmit": true -- TypeScript only type-checks; it doesn't output compiled .js files (Bun runs the .ts files directly).

Best practices

These options activate strict type-safety rules to catch potential bugs early.
  • "strict": true -- turns on TypeScript's full strict type-checking mode (strictNullChecks, noImplicitAny, etc. all at once).
  • "skipLibCheck": true -- skips type-checking inside .d.ts declaration files (faster builds, avoids errors in third-party type definitions you don't control).
  • "noFallthroughCasesInSwitch": true -- errors if a switch case falls through to the next case without a break/return.
  • "noUncheckedIndexedAccess": true -- accessing an object/array by index (e.g. arr[i]) returns T | undefined instead of just T, forcing you to handle the "might not exist" case. Useful in test code that reads array/object results.
  • "noImplicitOverride": true -- requires the override keyword when a subclass method overrides a parent class method (relevant if you're using Page Object classes with inheritance).

Stricter flags (disabled here)

These are options explicitly turned off to keep the development experience smoother and less strict during early coding phases.

  • "noUnusedLocals": false -- won't error on declared-but-unused local variables.
  • "noUnusedParameters": false -- won't error on unused function parameters (common in test fixtures/callbacks where you don't use every argument).
  • "noPropertyAccessFromIndexSignature": false -- allows dot notation (obj.foo) even on objects typed with an index signature, instead of forcing bracket notation (obj['foo']).
... This is what "bun init" produced. Let's investigate with Claude.ai or Google AI to see if we should now be all set with our Bun + Playwright + TypeScript project... 

TSConfig.json: Take Two!


After I ran it by GitHub Copilot, it suggested to change the tsconfig like so... 

  "compilerOptions": {
    // Environment setup & latest features
    "lib": ["ES2022", "DOM"],
    "target": "ES2022",
    "module": "ESNext",
    "moduleDetection": "force",
    "types": ["node", "bun"],


But Why, Claude? 

"These choices are better because they match the runtime and tooling you actually have, rather than using the most generic defaults.target: "ES2022": this targets a modern JavaScript version that is broadly supported and a good default for current Node/Bun environments. It is more practical than ESNext because it gives a stable, explicit baseline.
  • lib: ["ES2022", "DOM"]: this tells TypeScript about modern JavaScript features plus browser APIs like window, document, and fetch. That matters for Playwright tests, which often interact with browser-like environments.
  • module: "ESNext": this matches modern ESM-style projects and works well with current Bun/Node tooling. It is a better fit than older module systems when you are using modern package resolution.
  • types: ["node", "bun"]: this makes the compiler aware of both Node and Bun globals/types. That is useful because your project is using Bun as the package runner while also relying on Node-compatible APIs.
"In short, the new config is more explicit, more compatible with current tooling, and less likely to surprise you later when you add scripts, browser APIs, or ESM-style imports".

Add the TypeScript Checking to a Script

Okay, let's add the "tsc --noEmit" command to our package.json script:

  "scripts": {
    "test": "playwright test",
    "typecheck": "tsc --noEmit"
  },

Now, when you run "bun run typecheck", the TypeScript Compiler will test out your code. 

No errors? No output. No problem! 

With our next post, we will see other ways to add ways to review our code. 

Until then... Happy Testing!

-T.J. Maher
Software Engineer in Test

BlueSky | YouTubeLinkedIn | Articles

No comments:

Post a Comment