microsoft / microsoft/TypeScript

`checkJs` skips `.mjs`/`.cjs` beside a `.d.mts`/`.d.cts`, but not `.js` beside a `.d.ts`

Ouverte
#64,312 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Langage dominant
Go
Étoiles
111k
Forks
14.3k
Merge moyen
2 j 4 h
PR mergées (30 j)
132

Description

🔎 Search Terms

checkJs, allowJs, .d.mts, .d.cts, declaration file adjacent to implementation, file silently dropped from program, extension priority

🕗 Version & Regression Information

Reproduces identically on 5.9.3, 6.0.3, and 7.1.0-dev.20260916.1. Long-standing, not a regression.

⏯ Playground Link

Not applicable: needs two sibling files on disk plus moduleResolution: nodenext.

💻 Code

Three directories, identical but for extensions. Same tsconfig.json in each; package.json has "type": "module" only for the .mjs case.

index.js / index.mjs / index.cjs:

export const n = 1;

const bad = null;
bad.a.b.c();

index.d.ts / index.d.mts / index.d.cts:

export declare const n: number;
{
	"compilerOptions": {
		"target": "esnext",
		"module": "nodenext",
		"moduleResolution": "nodenext",
		"allowJs": true,
		"checkJs": true,
		"strict": true,
		"noEmit": true
	}
}
🙁 Actual behavior
index.js   + index.d.ts    ->  error TS18047: 'bad' is possibly 'null'.
index.mjs  + index.d.mts   ->  (no output)
index.cjs  + index.d.cts   ->  (no output)

The same authoring relationship — a hand-written declaration file beside its implementation — produces opposite outcomes based only on the file extension.

In the .mjs/.cjs cases the implementation is not merely unchecked, it is absent from the program: --listFiles reports only index.d.mts. In the .js case --listFiles reports both index.d.ts and index.js.

🙂 Expected behavior

The three rows should agree. Either all of them check the implementation, or none do.

I'd expect the .js row to be the correct one — the declaration file describes the module for consumers, and checkJs still checks the implementation — but consistency matters more than which way it goes.

Additional information

#47796 reported the .mjs/.mts variant and was closed as "Working as Intended", on the rationale that the JS file is a build artifact of the TS file, and that "the compiler only automatically pulls in one copy of a file with the same basename (the most TS-y one)".

That rationale doesn't extend to this case:

  • A .d.mts is not something a .mjs is emitted from, so the build-artifact framing doesn't apply. .d.ts + .js is the same relationship, and there the compiler does pull in both.
  • Neither does the one-copy-per-basename rule, for the same reason — index.js and index.d.ts are both in the program.

The failure is silent, which is what makes it expensive: a package shipping .mjs/.cjs with adjacent hand-written declarations gets no checking of its implementation at all, while every sibling module without a declaration file is checked normally. tsc exits 0 and CI is green. I found this only by deliberately breaking a file and noticing the build still passed. Per #47796, VS Code does report these errors, so the editor and CI silently disagree.

#64098 is the same silent-drop shape from wildcard include (hasFileWithHigherPriorityExtension), if the two are related.

So, concretely:

  1. Make the three rows consistent, or
  2. if this is intended, document which sibling extensions suppress checking, and emit a diagnostic when an input file is dropped from the program for this reason.

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par exécuter la reproduction dans trois répertoires avec tsc --listFiles et comparez les résultats pour .js/.d.ts, .mjs/.d.mts et .cjs/.d.cts. Suivez le comportement de priorité des extensions autour de hasFileWithHigherPriorityExtension, référencé dans #64098. Le travail est terminé lorsque les trois cas se comportent de manière cohérente et que le scénario de suppression silencieuse est couvert par des tests.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
javascript, typescript
Domaine
compilers
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Active
Clarté
Plutôt claire
Accessibilité débutants
52/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.