At a glance
Last verified 21 October 2026 with TypeScript 5.9.3: the defaults are pinned in this site's tests, and the editor lookup is read from TypeScript's source.
| Compared | jsconfig.json | tsconfig.json |
|---|---|---|
allowJs | true by default | false by default |
checkJs | false by default | false by default |
noEmit | true by default | false: tsc writes .js output unless told not to |
skipLibCheck | true by default | false by default |
maxNodeModuleJsDepth | 2: reads JavaScript packages for types | 0: JavaScript packages are untyped |
allowSyntheticDefaultImports | true by default | Follows esModuleInterop and module |
| Automatic type acquisition in editors | On by default | Off by default |
Found by tsc with no -p | No: run tsc -p jsconfig.json | Yes |
| Found by editors | Yes, after a tsconfig.json in the same folder | Yes, first in each folder |
Options and extends | The same options, schema and extends | The same options, schema and extends |
Same format, different defaults
A jsconfig.json is a tsconfig.json under another name. Every option, the schema and extends work the same way; what changes is a handful of defaults TypeScript applies when the file is named jsconfig.json: allowJs, noEmit, skipLibCheck and allowSyntheticDefaultImports on, and maxNodeModuleJsDepth at 2. Those defaults describe a JavaScript project that is checked and never built, which is why this site uses a jsconfig.json in every setup. This site’s tests read them from TypeScript 5.9 on every run.
checkJs is not among them: a jsconfig.json alone gives editors types for your JavaScript but reports no errors in it. Turn on checkJs, or put // @ts-check at the top of a file.
The default that changes results: packages
maxNodeModuleJsDepth: 2 means that when you import a JavaScript package that ships no types, tsc reads its JavaScript for types, two imports deep. With a tsconfig.json (depth 0) the same import is untyped. The examples show the same import both ways:
import { slugify } from "slugify-lite";
console.log(slugify(42));error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} import { slugify } from "slugify-lite";error TS7016: Could not find a declaration file for module 'slugify-lite'. '/project/node_modules/slugify-lite/index.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/slugify-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'slugify-lite';`
console.log(slugify(42));jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"maxNodeModuleJsDepth": 0
},
"include": [
"*.js"
]
} Neither is wrong: reading a package’s JavaScript gives you types inferred from code that was not written to be checked, which can be looser or stricter than you expect. The option’s page covers when to change it.
The default that bites: noEmit
A tsconfig.json without noEmit makes tsc write a .js file for every input file, next to it unless outDir is set. For a JavaScript project that means overwriting your sources is one missing option away (tsc refuses to overwrite an input file, with TS5055, but the error surprises people). A jsconfig.json cannot do that by default.
The default also travels through extends: a config that extends a jsconfig.json inherits its file-name defaults, so a second config that writes .d.ts files has to say "noEmit": false, as in the library setup:
export function double(n) {error TS7006: Parameter 'n' implicitly has an 'any' type.
return n * 2;
}jsconfig.json for this example
{
"extends": "./jsconfig.base.json",
"compilerOptions": {
"noEmit": true
},
"include": [
"*.js"
]
} How each file is found
tsc with no arguments looks for a tsconfig.json in the current folder and its parents; a jsconfig.json has to be named, tsc -p jsconfig.json, which is why every npm script on this site spells it out.
Editors find configs through TypeScript’s language service, which walks up from the file you open and, in each folder, takes a tsconfig.json before a jsconfig.json. A jsconfig.json next to a tsconfig.json is therefore ignored by the editor (TypeScript 5.9’s source; we read it rather than ran an editor). The language service also turns on automatic type acquisition for a jsconfig.json project by default: editors such as VS Code then download @types packages for your dependencies into a cache, which can give the editor types that tsc does not have. If the editor and npm run lint disagree about a package, install its @types package as a development dependency, so both read the same file.
Moving from one to the other
To turn a jsconfig.json into a tsconfig.json, rename it and add the defaults you relied on: "allowJs": true, "noEmit": true, "skipLibCheck": true, and "maxNodeModuleJsDepth": 2 if you import JavaScript-only packages. The other direction is the same list in reverse. The jsconfig generator writes checkJs (which implies allowJs), noEmit and skipLibCheck explicitly, so its output means nearly the same under either name; add maxNodeModuleJsDepth if you rename it and import JavaScript-only packages.
Which to choose
Choose jsconfig.json when…
- The project is JavaScript checked with JSDoc: its defaults (no output, JavaScript files included, packages read for types) are the ones you would set anyway.
- You want the file name to tell readers there is no build.
Choose tsconfig.json when…
- The project has
.tsfiles, or tsc writes output (.jsor.d.ts) as part of a build. - A tool you use looks only for
tsconfig.json, astsc -bwith project references and some bundlers and test runners do. - You want plain
tsc(no-p) to find the config.
Questions
Can I just rename a jsconfig.json to tsconfig.json?
Yes, if you write down the defaults you were relying on: allowJs, noEmit, skipLibCheck and, if you import JavaScript-only packages, maxNodeModuleJsDepth. Without noEmit, tsc starts writing .js files next to yours.
What if a folder has both files?
tsc uses the one you name with -p. Editors that run TypeScript's language service look for tsconfig.json first in each folder, so in TypeScript 5.9 a jsconfig.json beside a tsconfig.json is ignored by the editor.
Does a config that extends a jsconfig.json keep its defaults?
Yes. The defaults come from the file name of the base, so jsconfig.types.json extending jsconfig.json inherits noEmit: true, which is why a declaration-writing config has to set noEmit: false.
Sources
- TSConfig Reference · TypeScript
- jsconfig.json Reference · Visual Studio Code docs
- TSConfig Reference: maxNodeModuleJsDepth · TypeScript
- TSConfig Reference: typeAcquisition · TypeScript
Last reviewed 21 October 2026.