Use checkJs in TypeScript

checkJs enables type checking in JavaScript files. Learn how to enable it, what errors it surfaces, and when to use it during migration.

5 min read

checkJs tells TypeScript to report type errors in JavaScript files, not just TypeScript files. It requires allowJs to be enabled first, since TypeScript has to accept JavaScript files as inputs before it can check them at all.

With checkJs turned on, your existing JavaScript gets the same kind of feedback TypeScript gives a TypeScript file. You do not rename a single file to get that feedback; the checker just starts reading the JavaScript that is already there.

How checkJs Works

When both allowJs and checkJs are on, TypeScript applies its normal type inference to every JavaScript file and reports whatever errors it finds. It reads JSDoc comments when they are present and falls back to plain inference when a file has none.

The example below is a small JavaScript file that looks completely fine on a quick read, since nothing about it stands out as wrong to a human eye.

javascriptjavascript
// utils.js
module.exports.pi = parseFloat(3.142);

Without checkJs, this compiles silently, and Node would still run it without complaint at runtime. With checkJs turned on, TypeScript instead reports a compile-time error before the file ever ships:

texttext
Argument of type 'number' is not assignable to parameter of type 'string'.

The built-in parseFloat function expects a string argument, but 3.142 is a number literal, not a string. TypeScript does not coerce the number for you, so it flags the call as a type mismatch.

This is a real bug that plain JavaScript would silently accept, since Node never rejects a number passed where a string was expected.

Enable checkJs

Add it to a project's TypeScript configuration file alongside allowJs:

jsonjson
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

Once this is saved, running the compiler checks every JavaScript file under the source folder, and any type error is reported the same way it would be for a TypeScript file. The compiler does not skip a file just because it still has a JavaScript extension.

If the output feels overwhelming on a large existing codebase, you have two ways to narrow the scope:

  1. Turn checkJs off globally and use @ts-check in individual files instead.
  2. Keep checkJs on but add @ts-nocheck at the top of files you are not ready to fix yet.

Using @ts-check Per File

The @ts-check comment enables type checking for a single JavaScript file, even when checkJs is off in the project configuration. This is useful when you want to check a new or critical file without touching every other file in the project at once.

Add the comment at the very top of a file, above everything else:

javascriptjavascript
// @ts-check
 
function add(a, b) {
  return a + b;
}
 
add(5, "10");

Neither parameter has a type, so TypeScript cannot infer one from a single definition. A checked file treats an untyped parameter as an implicit any, and by default that is reported as an error:

texttext
Parameter 'a' implicitly has an 'any' type.
Parameter 'b' implicitly has an 'any' type.

The call itself is not flagged here, because without any type information TypeScript cannot tell that mixing a number and a string is wrong. Adding JSDoc types to the function fixes both problems at once, as the next section shows.

The opposite comment, @ts-nocheck, suppresses every type error in a file no matter what the project configuration says. Reach for it on legacy files you are not ready to touch yet, not as a permanent fix.

What checkJs Catches

checkJs surfaces the same categories of errors that TypeScript catches in TypeScript files, using inference and any JSDoc comments it finds along the way:

Error categoryExampleCaught by checkJs?
Wrong argument typeMath.floor("hello")Yes
Missing propertya typo like user.nammeYes, if the shape is known
Possibly null or undefinedreading a property on a value that might be nullYes, unless strictNullChecks is turned off
Unannotated parametera function parameter with no type informationYes, unless noImplicitAny is turned off
Excess property in an object literalpassing an extra field that is not in the expected shapeYes

The quality of checking depends on how much type information TypeScript can work with. Plain JavaScript with no JSDoc still gets useful checking for simple functions and object shapes. Complex APIs benefit from adding JSDoc so the checker has more to go on.

checkJs With JSDoc Annotations

When a file has JSDoc comments above a function, checkJs reads them as real type annotations. This gives precise checking without converting the file to TypeScript, which matters when a full rewrite is not practical yet.

javascriptjavascript
// @ts-check
/**
 * @param {string} name
 * @param {number} age
 * @returns {string}
 */
function describe(name, age) {
  return `${name} is ${age} years old`;
}
 
describe("Alice", "ten");

Because the JSDoc comment says the age parameter is a number, passing the string "ten" is now a clear mismatch. TypeScript reports it the same way it would for a typed function:

texttext
Argument of type 'string' is not assignable to parameter of type 'number'.

This is the same error message a TypeScript file would produce for an equivalent typed function. The JSDoc comment is doing the same job a type annotation does in TypeScript source, just written as a comment instead of syntax.

For more on typing JavaScript with comments, see Type JavaScript with JSDoc.

checkJs and Strict Compiler Options

checkJs already applies the same implicit-any and null checks that a typed TypeScript file gets, even without adding extra strict settings to the configuration file. If a codebase feels too forgiving under checkJs, the usual cause is that those checks were explicitly turned off somewhere, not that they were never on.

To make the intent explicit and protect it from being loosened elsewhere in the configuration, add the settings directly:

jsonjson
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

With noImplicitAny set this way, every unannotated function parameter in a checked file is guaranteed to stay an error, which pushes the codebase toward adding JSDoc types over time. The next example shows that same rule catching a two-parameter function with no annotations at all.

javascriptjavascript
// @ts-check
 
function multiply(a, b) {
  return a * b;
}

Neither parameter has a type, so noImplicitAny reports both of them instead of letting the file compile silently. Running the compiler on this file produces two separate errors, one for each parameter:

texttext
Parameter 'a' implicitly has an 'any' type.
Parameter 'b' implicitly has an 'any' type.

The fix is either a JSDoc annotation for each parameter or a full conversion to a TypeScript file. Either approach gives the checker enough information to verify how the function is actually called elsewhere.

When to Use checkJs

ScenarioRecommendation
You just added TypeScript to a projectKeep checkJs off initially
You want to find bugs in existing JSEnable checkJs globally
Too many errors from old codeUse @ts-check per file instead
You are actively migrating a fileUse @ts-check until you rename it to TypeScript
CI pipelineEnable checkJs to prevent regressions in JavaScript files

A common migration pattern is to enable checkJs globally and add @ts-nocheck at the top of legacy files that are not ready to fix yet. Remove that comment when the team finally tackles the file, so it rejoins normal checking.

Common Mistakes

Using checkJs without allowJs. checkJs has no effect unless allowJs is also enabled. Without allowJs, TypeScript ignores JavaScript files entirely, so there is nothing for checkJs to check.

Expecting checkJs to add types. checkJs checks existing code; it does not add types to a project on its own. The developer still owns the annotations and the fixes.

For stricter checking, add noImplicitAny and strictNullChecks explicitly. See Strict Mode in TypeScript.

Assuming a compile-time error stops the code from running. checkJs errors are reported at compile time and in the editor. They do not block a JavaScript file from executing under Node or in a browser unless a separate build step is configured to fail on type errors.

Treat checkJs output as a warning system to fix, not a runtime guard. For more on tightening these checks, see NoImplicitAny in TypeScript.

Rune AI

Rune AI

Key Insights

  • checkJs enables type checking in .js files when allowJs is also enabled.
  • It catches the same kinds of errors TypeScript catches: wrong argument types, missing properties, null issues.
  • Use // @ts-check at the top of a file to enable checking for just that file.
  • JSDoc annotations let you add types to JavaScript without converting to .ts.
  • Start with checkJs off, then enable it gradually as you fix errors.
RunePowered by Rune AI

Frequently Asked Questions

Is checkJs the same as converting files to .ts?

No. checkJs reports type errors in .js files, but the files remain JavaScript. You get type feedback without renaming files. Converting to .ts gives you richer type syntax and stricter checks.

Can checkJs be too strict for my codebase?

You can turn it on and off per file. If checkJs produces too many errors, keep it off globally and use // @ts-check at the top of individual files you want to check.

Do I need allowJs for checkJs to work?

Yes. checkJs requires allowJs to be enabled. Without allowJs, TypeScript ignores .js files entirely and checkJs has no effect.

Conclusion

checkJs brings TypeScript's type checking to JavaScript files without requiring a rename. Use it to find bugs in existing JavaScript, catch mistakes during migration, and get editor feedback in .js files. Enable it globally for full coverage or per file with // @ts-check for a gentler approach.