In a tsconfig.json, what does the TypeScript compiler option `allowJs` do on its own, and what changes when you also turn on `checkJs`?
answer
- two flags, two different jobs
- one admits files, the other checks them
- inference happens either way
- emit means output can hit input
- per-file comments override the project switch
basics
~20 sallowJs admits .js and .jsx files into the TypeScript program so they are resolved, inferred from and emitted — but no errors are reported in them. checkJs additionally turns the type checker on for those JavaScript files.
solid answer
~50 sBy default `tsc` only puts `.ts`, `.tsx` and `.d.ts` files into the program. `allowJs` adds `.js` and `.jsx`: they become real inputs, so imports to them resolve, the compiler infers types from their code and JSDoc, and it emits them through the normal pipeline honouring `target` and `module`. What `allowJs` does *not* do is report a single type error inside them. `checkJs` is the second half — it tells the checker to report diagnostics in JavaScript files exactly as it does in TypeScript files, using whatever it can infer plus JSDoc comments. `checkJs` only means something once `allowJs` has admitted the files; there is nothing to check otherwise. You can also opt individual files in or out with the `// @ts-check` and `// @ts-nocheck` comments. One practical trap: because `allowJs` makes `tsc` emit JavaScript, you need an `outDir` distinct from your sources or `noEmit`, otherwise output would land on top of the inputs.
code
json · 11 lines{
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"strict": true,
"target": "ES2022",
"module": "ESNext",
"outDir": "./dist"
},
"include": ["src"]
}go deeper
Know the one-line split: allowJs lets JavaScript into the compilation, checkJs makes the compiler complain about it. Say plainly that allowJs alone reports no errors.
Explain the mechanics: the program's accepted extensions, inference from JSDoc and code, emit through target/module, and why an outDir separate from the sources is needed once JavaScript is an input.
Show judgment about rollout: allowJs first so TypeScript consumers stop seeing any, then checking file-by-file with // @ts-check, and a non-emitting type-check job so a checking failure never blocks the build pipeline.
Own the policy question — whether JavaScript stays a permanent first-class citizen of the build or is a transitional state, what the compiler is authoritative for versus the bundler, and how that choice shapes build time and CI topology.
## What "the program" is When `tsc` runs, it first builds a **program**: the set of source files it will parse and reason about. That set starts from `files`/`include` in `tsconfig.json` and grows transitively through every import it can resolve. Out of the box the program only accepts `.ts`, `.tsx` and `.d.ts`. A `.js` file sitting next to them is invisible to the compiler. That invisibility has a consequence people meet before they ever read the flag list: from a `.ts` file, `import { format } from './util.js'` finds no declaration for `./util`, so the import is an implicit `any` — and an outright error under `noImplicitAny`. ## allowJs: admission, inference, emit `allowJs: true` adds `.js` and `.jsx` to the accepted extensions. Three things follow. **Resolution.** Imports pointing at JavaScript now resolve to real source files instead of dead ends. **Inference.** TypeScript reads the JavaScript and infers types from it — literal initialisers, function bodies, and any JSDoc comments already present. Consumers do not get `any`; they get a best-effort signature. This is why `allowJs` alone is often worth turning on: your existing TypeScript gains real types for the JavaScript it calls. **Emit.** The `.js` files are now compiler *inputs*, so they are also compiler *outputs*: downlevelled to `target`, module syntax rewritten per `module`, comments and helpers handled the same way. Since inputs and outputs are both JavaScript, they can collide, and the compiler refuses rather than clobbering your source: ``` error TS5055: Cannot write file '/app/src/util.js' because it would overwrite input file. ``` The fix is an `outDir` that is not the source directory, or `noEmit: true` if another tool (a bundler, `tsx`, Babel) does the actual building and `tsc` is only your type-check gate. What `allowJs` deliberately does **not** do is report errors in those files. A `.js` file can call an undefined function or pass a string where a number is expected and the build stays green. ## checkJs: turning the checker on `checkJs: true` flips that last part: the checker now reports diagnostics in JavaScript files with the same rules it applies to TypeScript. Unresolved names, wrong argument types, property access on a possibly-undefined value under `strictNullChecks` — all of it surfaces. The rules are the same; the *information* is different. A `.js` file cannot carry type syntax — `function f(x: string)` is a syntax error in JavaScript — so everything the checker knows comes from three places: the code itself, initialisers, and JSDoc comments. Adding `@param`/`@returns`/`@typedef` is how you feed it more. `checkJs` is a project-wide switch, but checking is decided per file, and two comments override it: ```js // @ts-check // this file is checked even when checkJs is off ``` ```js // @ts-nocheck // this file is skipped even when checkJs is on ``` That pairing is what makes the flag usable on a large codebase: turn `checkJs` on and exempt the handful of files that are not ready, or leave it off and opt files in one at a time. ## Choosing a combination - **Neither flag.** A pure TypeScript project. Any stray `.js` is outside the program. - **`allowJs` only.** JavaScript participates and is emitted, TypeScript consumers get inferred types, nothing in the JavaScript is enforced. A very cheap first step. - **`allowJs` + `checkJs`.** The JavaScript is genuinely type-checked. Expect errors immediately; that is the point. - **`allowJs` + `// @ts-check` per file.** Same enforcement, file by file, with the project switch still off. The strictness flags stack on top of whichever you choose: with `strict` on, a checked `.js` file is held to `strictNullChecks` and the rest just like a `.ts` file. ## Editors The same machinery powers editors that use the TypeScript language service, which is why a plain `.js` file with `// @ts-check` at the top starts showing red squiggles in VS Code with no build step involved. The compiler flags decide what your *build* enforces; the comment decides what a single file enforces everywhere.
- With `allowJs` on but `checkJs` off, what type does a `.ts` file see when it imports a function from a `.js` file?An inferred signature, not `any`. The compiler parses the JavaScript, infers parameter and return types from the body, defaults and any JSDoc, and hands that to the importer. It simply never reports errors found *inside* that JavaScript file. Without `allowJs` at all, the import has no declaration to read and degrades to an implicit `any`.
- Right after enabling `allowJs`, `tsc` fails with "Cannot write file ... because it would overwrite input file". What happened?The `.js` sources are now compiler inputs, so the compiler wants to emit `.js` outputs — and with no `outDir` (or an `outDir` equal to the source root) the output path is the input path. Point `outDir` at a separate build directory, or set `noEmit: true` if `tsc` is only your type-check gate and something else builds.
- Does turning on `checkJs` change what the compiler emits?No. `checkJs` is purely a diagnostics switch; emit is governed by `allowJs`, `target`, `module`, `outDir` and friends. A project can fail type-checking under `checkJs` and still produce identical output to before — which is exactly why teams sometimes run `checkJs` in a separate non-emitting check job.
saying these in an interview costs you the question
- Says allowJs type-checks JavaScript files by itself
- Thinks JavaScript files import as any even with allowJs on
- Believes allowJs rewrites .js files into TypeScript
- Expects checkJs to work without the files being in the program
- Thinks you can write TypeScript annotations in a checked .js file