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.
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.
// 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:
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:
{
"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:
- Turn checkJs off globally and use
@ts-checkin individual files instead. - Keep checkJs on but add
@ts-nocheckat 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:
// @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:
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 category | Example | Caught by checkJs? |
|---|---|---|
| Wrong argument type | Math.floor("hello") | Yes |
| Missing property | a typo like user.namme | Yes, if the shape is known |
| Possibly null or undefined | reading a property on a value that might be null | Yes, unless strictNullChecks is turned off |
| Unannotated parameter | a function parameter with no type information | Yes, unless noImplicitAny is turned off |
| Excess property in an object literal | passing an extra field that is not in the expected shape | Yes |
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.
// @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:
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:
{
"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.
// @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:
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
| Scenario | Recommendation |
|---|---|
| You just added TypeScript to a project | Keep checkJs off initially |
| You want to find bugs in existing JS | Enable checkJs globally |
| Too many errors from old code | Use @ts-check per file instead |
| You are actively migrating a file | Use @ts-check until you rename it to TypeScript |
| CI pipeline | Enable 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
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.
Frequently Asked Questions
Is checkJs the same as converting files to .ts?
Can checkJs be too strict for my codebase?
Do I need allowJs for checkJs to work?
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.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.