Skip to content

TS5097 is not reported for require() in checked JS or for clause-less side-effect imports #64028

Description

@ekazakov14

Regression from typescript@6.0.2 to typescript@7.0.2. Also reproduces on @typescript/native-preview@7.0.0-dev.20260707.2 and on current main. node v24.13.0, darwin/arm64.

Steps to reproduce

Three files, no tsconfig.json:

lib.ts

export const value = 1;

require.js

// @ts-check
const { value } = require('./lib.ts');
console.log(value);

side-effect.ts

import './lib.ts';
tsc --noEmit --allowJs require.js side-effect.ts

Behavior with typescript@6.0.2

require.js(2,27): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
side-effect.ts(1,8): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.

Behavior with typescript@7.0.2

No output — both are silently accepted.

This is not a module-resolution difference: 7.0 resolves require('./lib.ts') and type-checks against it. Changing console.log(value) to value(123) yields the same TS2349 from both versions at the same position — only TS5097 is missing.

Root cause

The two versions guard the diagnostic differently:

7.0 — tsc/internal/checker/checker.go:15334    if ast.FindAncestor(location, ast.IsEmittableImport) != nil {
6.0 — src/compiler/checker.ts:4779             if (errorNode && !(importOrExport?.isTypeOnly || findAncestor(location, isImportTypeNode))) {

7.0 asks "will this import survive emit?"; 6.0 asks "is this explicitly type-only?". The answers differ for exactly two shapes: a CommonJS require(), which is a plain CallExpression that IsEmittableImport never matched, and a clause-less side-effect import, which stopped matching in typescript-go#1198 — a fix for typescript-go#1190 in the adjacent .d.ts branch, which shares the same predicate.

Full matrix

case 6.0.2 7.0.2
require('./lib.ts') in checked JS TS5097
import './lib.ts' TS5097
import { value } from './lib.ts' TS5097 TS5097
import { value } from './lib.ts' in JS TS5097 TS5097
export { value } from './lib.ts' TS5097 TS5097
import('./lib.ts') TS5097 TS5097
import lib = require('./lib.ts') TS5097 TS5097
import type { Thing } from './lib.ts'
export type { Thing } from './lib.ts'
import('./lib.ts').Thing in type position
import './types.d.ts'

The versions disagree only on the first two rows; the last four are negative cases where both correctly stay silent.

Metadata

Metadata

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions