Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent typechecking with require() in JS and TS #60032

Open
u130b8 opened this issue Sep 23, 2024 · 5 comments
Open

Inconsistent typechecking with require() in JS and TS #60032

u130b8 opened this issue Sep 23, 2024 · 5 comments
Labels
Has Repro This issue has compiler-backed repros: https://aka.ms/ts-repros Needs More Info The issue still hasn't been fully clarified

Comments

@u130b8
Copy link

u130b8 commented Sep 23, 2024

πŸ”Ž Search Terms

require import js ts module esm esmodule cjs commonjs

πŸ•— Version & Regression Information

  • This happens in the nightly version of TS

⏯ Playground Link

Multiple files not supported in playground, see bug workbench

πŸ’» Code

// @types: ["node"]
// @allowJs
// @checkJs

// @filename: module-cjs-js.js
const Value = "module-cjs-js";
module.exports = { Value };

// @filename: module-cjs-ts.ts
const Value = "module-cjs-ts";
module.exports = { Value };

// @filename: module-esm-js.js
const Value = "module-esm-js";
export { Value };

// @filename: module-esm-ts.ts
const Value = "module-esm-ts";
export { Value };

// @filename: main-js.js
const ConstRequireCjsJs = require("./module-cjs-js");
const ConstRequireEsmJs = require("./module-esm-js");
const ConstRequireCjsTs = require("./module-cjs-ts");
const ConstRequireEsmTs = require("./module-esm-ts");
console.log(ConstRequireCjsJs.Value); // (alias) const Value: "module-cjs-js"
//                            ^?
console.log(ConstRequireEsmJs.Value); // (alias) const Value: "module-esm-js"
//                            ^?
console.log(ConstRequireCjsTs.Value); // Error: Property 'Value' does not exist on type 'typeof import("./module-cjs-ts")'
//                            ^?
console.log(ConstRequireEsmTs.Value); // (alias) const Value: "module-esm-ts"
//                            ^?

import * as ImportFromCjsJs from "./module-cjs-js";
import * as ImportFromEsmJs from "./module-esm-js";
import * as ImportFromCjsTs from "./module-cjs-ts";
import * as ImportFromEsmTs from "./module-esm-ts";
console.log(ImportFromCjsJs.Value); // (alias) const Value: "module-cjs-js"
//                          ^?
console.log(ImportFromEsmJs.Value); // (alias) const Value: "module-esm-js"
//                          ^?
console.log(ImportFromCjsTs.Value); // Error: Property 'Value' does not exist on type 'typeof import("./module-cjs-ts")'
//                          ^?
console.log(ImportFromEsmTs.Value); // (alias) const Value: "module-esm-ts"
//                          ^?

// @filename: main-ts.ts
const ConstRequireCjsJs = require("./module-cjs-js");
const ConstRequireEsmJs = require("./module-esm-js");
const ConstRequireCjsTs = require("./module-cjs-ts");
const ConstRequireEsmTs = require("./module-esm-ts");
console.log(ConstRequireCjsJs.Value); // any
//                            ^?
console.log(ConstRequireEsmJs.Value); // any
//                            ^?
console.log(ConstRequireCjsTs.Value); // any
//                            ^?
console.log(ConstRequireEsmTs.Value); // any
//                            ^?

import * as ImportFromCjsJs from "./module-cjs-js";
import * as ImportFromEsmJs from "./module-esm-js";
import * as ImportFromCjsTs from "./module-cjs-ts";
import * as ImportFromEsmTs from "./module-esm-ts";
console.log(ImportFromCjsJs.Value); // (alias) const Value: "module-cjs-js"
//                          ^?
console.log(ImportFromEsmJs.Value); // (alias) const Value: "module-esm-js"
//                          ^?
console.log(ImportFromCjsTs.Value); // Error: Property 'Value' does not exist on type 'typeof import("./module-cjs-ts")'
//                          ^?
console.log(ImportFromEsmTs.Value); // (alias) const Value: "module-esm-ts"
//                          ^?

import ImportRequireCjsJs = require("./module-cjs-js");
import ImportRequireEsmJs = require("./module-esm-js");
import ImportRequireCjsTs = require("./module-cjs-ts");
import ImportRequireEsmTs = require("./module-esm-ts");
console.log(ImportRequireCjsJs.Value); // (alias) const Value: "module-cjs-js"
//                             ^?
console.log(ImportRequireEsmJs.Value); // (alias) const Value: "module-esm-js"
//                             ^?
console.log(ImportRequireCjsTs.Value); // Error: Property 'Value' does not exist on type 'typeof import("./module-cjs-ts")'
//                             ^?
console.log(ImportRequireEsmTs.Value); // (alias) const Value: "module-esm-ts"
//                             ^?

Workbench Repro

πŸ™ Actual behavior

Type resolution is inconsistent when using require() from .js and .ts files:

  • CommonJS modules with .ts extension have no properties regardless of import type (no error if ESModule.ts or CommonJS.js)
  • Using require() in a .ts file is always unchecked (checked in a .js file or in .ts file with import X = syntax)
MainExt Type Ext RequireOrImport Issue
JS CJS JS const X = require("Y")
JS ESM JS const X = require("Y")
JS CJS TS const X = require("Y") Error
JS ESM TS const X = require("Y")
JS CJS JS import * as X from "Y"
JS ESM JS import * as X from "Y"
JS CJS TS import * as X from "Y" Error
JS ESM TS import * as X from "Y"
JS CJS JS const X = require("Y")
JS ESM JS const X = require("Y")
JS CJS TS const X = require("Y") Error
JS ESM TS const X = require("Y")
TS CJS JS const X = require("Y") Any
TS ESM JS const X = require("Y") Any
TS CJS TS const X = require("Y") Any
TS ESM TS const X = require("Y") Any
TS CJS JS import * as X from "Y"
TS ESM JS import * as X from "Y"
TS CJS TS import * as X from "Y" Error
TS ESM TS import * as X from "Y"
TS CJS JS import X = require("Y")
TS ESM JS import X = require("Y")
TS CJS TS import X = require("Y") Error
TS ESM TS import X = require("Y")

πŸ™‚ Expected behavior

I expected require() to be typechecked in .ts files because it's typechecked in .js files.
I expected imports of CommonJS .ts files to work because CommonJS .js files work and are typechecked.

Additional information about the issue

This problem happens when porting an existing Node.JS codebase that uses CommonJS require() modules to TypeScript. It's not possible to port the code without also forcing it into ES Modules because:

  • CommonJS .ts files don't work
  • require() from .ts files is not typechecked
@YehuditLiba
Copy link

Have you tried changing your tsconfig.json settings and using esModuleInterop?"

@RyanCavanaugh
Copy link
Member

I expected require() to be typechecked in .ts files because it's typechecked in .js files.

Only import i = require( is supported in TS

I expected imports of CommonJS .ts files to work because CommonJS .js files work and are typechecked.

What specifically is the repro for this?

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Sep 25, 2024
@u130b8
Copy link
Author

u130b8 commented Sep 25, 2024

Bug workbench doesn't seem to work with types: ["node"], but you can repro it like this:

// @types: ["node"]

// @filename: test-cjs.ts
module.exports = { Value: 1 };

// @filename: main.ts
import test = require("./test-cjs");
test.Value; //  Error: Property 'Value' does not exist on type 'typeof import("./test-cjs")'
//   ^? any

If the module has a JS extension, then the import works. But if the module has a TS extension, it no longer works.

Only import i = require( is supported in TS

Is it possible to add support for const i = require( as well?
It seems strange because the functionality is there, the const require works and gets typechecked, but only with a JS extension, why not have it work if the file extension is TS as well? It would make the developer experience much better, and const require is much more flexible than import require:

// Can't do this with import X = require()
let X;
if (cond) {
    X = require("x-impl-1");
}
else {
    X = require("x-impl-2");
}

@typescript-bot typescript-bot added the Has Repro This issue has compiler-backed repros: https://aka.ms/ts-repros label Sep 25, 2024
@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 26, 2024

πŸ‘‹ Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript.


Issue body code block by @u130b8

❌ Failed: -

  • Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.

Historical Information
Version Reproduction Outputs
5.2.2, 5.3.2, 5.4.2, 5.5.2, 5.6.2

❌ Failed: -

  • Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • File '/home/runner/work/TypeScript/TypeScript/module-cjs-ts.ts' is not a module.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 26, 2024

πŸ‘‹ Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of this repro running against the nightly TypeScript.


Comment by @u130b8

❌ Failed: -

  • Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • File '/home/runner/work/TypeScript/TypeScript/test-cjs.ts' is not a module.

Historical Information
Version Reproduction Outputs
5.2.2, 5.3.2, 5.4.2, 5.5.2, 5.6.2

❌ Failed: -

  • Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
  • Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.
  • File '/home/runner/work/TypeScript/TypeScript/test-cjs.ts' is not a module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Has Repro This issue has compiler-backed repros: https://aka.ms/ts-repros Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

4 participants