You are currently encountering a rather obscure "feature" within the tsc
compiler.
Take a look at this example tsconfig
:
{
"include": [
"**/*.js",
"**/*.ts"
],
"compilerOptions": {
"rootDir": ".",
"listFiles": true,
"module": "commonjs",
"allowJs": true,
"checkJs": true,
"noEmit": true,
"skipLibCheck": true
}
}
$ tree -L 2
.
├── node_modules
│ ├── reverse-line-reader
│ └── typescript
├── package.json
├── src
│ ├── person.d.ts
│ └── person.js
├── tsconfig.json
└── yarn.lock
Execute tsc
:
/home/user/source/module/node_modules/typescript/lib/lib.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.es5.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.dom.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.webworker.importscripts.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.scripthost.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.legacy.d.ts
/home/user/source/module/src/person.d.ts
Only person.d.ts
is recognized by the compiler, despite the existence of person.js
. No errors are reported.
If you remove the include
section from tsconfig.json
:
$ tsc
src/person.js:7:5 - error TS2322: Type '{ name2: string; }' is not assignable to type 'Person'.
Object literal may only specify known properties, but 'name2' does not exist in type 'Person'. Did you mean to write 'name'?
7 name2: "sdf", // <-- this should error, but does not
~~~~~~~~~~~~
/home/user/source/module/node_modules/typescript/lib/lib.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.es5.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.dom.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.webworker.importscripts.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.scripthost.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.legacy.d.ts
/home/user/source/module/src/person.d.ts
/home/user/source/module/src/person.js
Found 1 error in src/person.js:7
Now, magically, both person.js
and person.d.ts
are picked up by the compiler. How?
It seems that when utilizing the include
directive, tsc
tends to exclude files it believes might have been previously generated by the compiler. For instance, running tsc
on a person.ts
file will produce output files named person.js
and person.d.ts
(with declaration
set to true
in tsconfig.json
).
The only mention I've come across of this behavior is in this document, although now considered deprecated:
Keep in mind that the compiler excludes files that could potentially be outputs; for instance, if input includes index.ts, then index.d.ts and index.js will be excluded. In general, having files with similar extensions next to each other is discouraged.
Despite being outdated, this behavior still persists today.
In conclusion, one solution is to eliminate the include
section entirely. It's unnecessary in simpler cases, such as this one. Alternatively, as suggested in the documentation, relocate your types to a separate folder or consolidate them into a single file.