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

"Cannot redeclare block-scoped variable" in separate files with declaration of same name #47229

Closed
eunakria opened this issue Dec 23, 2021 · 7 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@eunakria
Copy link

Bug Report

🔎 Search Terms

error TS2451: Cannot redeclare block-scoped variable

🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed the FAQ for entries about block-scoped variables and shadowing.

⏯ Playground Link

No playground link is available, since the bug is specific to behavior across multiple files.

💻 Code

package.json

{
        "name": "test",
        "devDependencies": {
                "@types/node": "^17.0.4",
                "typescript": "^4.0.0"
        }
}

tsconfig.json

{
        "include": ["*.ts"],
        "compilerOptions": {
                "strict": true
        }
}

index.ts

const b = require('./b')
const c = require('./c')
console.log(b(c))

b.ts

const c = require('./c')
module.exports = (x: any) => x * c

c.ts

module.exports = 3

🙁 Actual behavior

JS files index.js, b.js, c.js are emitted and do produce the expected result, but at compile time, the following errors are thrown:

b.ts:1:7 - error TS2451: Cannot redeclare block-scoped variable 'c'.

1 const c = require('./c')
        ~

  index.ts:2:7
    2 const c = require('./c')
            ~
    'c' was also declared here.

index.ts:2:7 - error TS2451: Cannot redeclare block-scoped variable 'c'.

2 const c = require('./c')
        ~

  b.ts:1:7
    1 const c = require('./c')
            ~
    'c' was also declared here.


Found 2 errors.

Running node index.js does, indeed, yield 9.

🙂 Expected behavior

Code compiles without error, since there is only one declaration of c per file.

@MartinJohns
Copy link
Contributor

MartinJohns commented Dec 23, 2021

When you don't have a import or export statement your file is considered a script, not a module, and scripts share a global scope. See the documentation: https://www.typescriptlang.org/docs/handbook/modules.html

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).

So this is working as intended.

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Jan 5, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.

@bobvanderlinden
Copy link

I have the same issue. It might be working as intended, but it's unclear how this needs to be configured in tsconfig.

@funnylookinhat
Copy link

I stumbled on this issue as well, and it really feels like the following aren't working as intended in a tsconfig for node projects:

    "module": "commonjs",
    "esModuleInterop": true,
    "moduleResolution": "node",

And that's adding a bit on top of the recommended config from here: https://www.npmjs.com/package/@tsconfig/node16

The best solution I found was to do one of two things:

a) Add a useless export to the files that are not working, e.g. export {}
b) Add export statements to everything you're ultimately exporting with module.exports

I suspect that doing the latter would make it easier to transition to ESM when they are no longer experimental. There's an added benefit if you're using the latest version of TS in that you can prefer exporting your interfaces right away, and lean on import type ... to use them at compile time, but exclude them from actual code.

In the end, we're just straddling that awkward in-between space of ESM + CommonJS, so no solution will feel great. :)

@bradfloodx
Copy link

Change your import export statements from node style to ES style:

  1. const b = require('./b') to import b from './b';
  2. module.exports = someVar to export default someVar

@jordanbtucker
Copy link

Is there a way to force TypeScript to see files without an import or an export as modules instead of scripts?

@xiaobao66
Copy link

xiaobao66 commented Jan 9, 2023

Is there a way to force TypeScript to see files without an import or an export as modules instead of scripts?

you can set moduleDetection config option

see: https://stackoverflow.com/a/74968079

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

9 participants