ES2015 module syntax is preferred over custom TypeScript modules and namespaces @typescript-eslint/no-namespace

I’m receiving the following error running npm start:

ES2015 module syntax is preferred over custom TypeScript modules and namespaces @typescript-eslint/no-namespace

    namespace InternalThings {...}

I tried to research this but it’s very confusing.

Why does this is happening?
How to fix it?

I tried to put some flags on my tsconfig.json but so far no success;

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

This is a lint error, caused by this lint rule: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-namespace.md

If you find the rule useful and want to keep it, then you’ll need to modify your code to use import and export instead of namespace. See the documentation of the rule for what counts as a fix.

If you like the rule, but want to disable the rule for this line, add the following just above it:

// eslint-disable-next-line @typescript-eslint/no-namespace

If you don’t like the rule and want to disable it entirely, then edit your .eslintrc file to have the following line:
rules: {
  "@typescript-eslint/no-namespace": "off"
}

Method 2

To fix this error, instead of:

export namespace InternalThings {
    export function myFunction() {
    }

    export class MyClass {
    }
}

import { InternalThings } from './internal-things';

InternalThings.myFunction();

you expose all the members of the namespace directly:
export function myFunction() {
}

export class MyClass {
}

and you import it like this:
import * as InternalThings from './internal-things';

InternalThings.myFunction();

The main idea is that users of your module can import only what they want, or name your module differently:
import * as CustomModuleName from './internal-things';

CustomModuleName.myFunction();

import { MyClass } from './internal-things';

let field = new MyClass();

Method 3

The error is coming from eslint. You have to either ignore ‘@typescript-eslint/no-namespace’ rule in the config or rewrite your code using ES6.

Custom TypeScript modules (module foo {}) and namespaces (namespace
foo {}) are considered outdated ways to organize TypeScript code.
ES2015 module syntax is now preferred (import/export)

Refer https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-namespace.md

Method 4

Fix Lint Error While Maintaining Same API

If you would like to handle the lint error without breaking any current implementations you can do the following, but you should really look at the above answer before committing to this: https://stackoverflow.com/a/63574739/349659

Before

Implementation

export namespace Container {
  export function someCall() { }
  export function anotherCall() { }
}

Consumer
import { Container } from './Container'

Container.someCall()
Container.anotherCall()

After

Option 1

// These are essentially private
function someCall() { }
function anotherCall() { }

// We expose them here
// This feels like a step towards CommonJS, but is valid ES Module code
export const Container = {
  someCall,
  anotherCall,
}

Option 2

You could also define and encapsulate the function calls directly into the object as well like so:

export const Container = {
  someCall() {},
  anotherCall() {},
}

In Conclusion

If you have a large codebase and want to “quickly” appease your linter, you can do a refactor like above. Make sure to consider this answer https://stackoverflow.com/a/63574739/349659 and the reasoning behind it.

At the end of the day, the quickest fix that requires no code change is to simply turn off this linting rule as mentioned in this answer: https://stackoverflow.com/a/58271234/349659

If you’re starting from scratch and run into this issue I would consider utilizing the modern implementation as the linter hints towards, but you may find that you enjoy namespaces and simply want them as well. If you’re part of a team you may want to get their feedback first and and follow a team standard.


Edge Cases & Considerations

One case I’ve ran into is having multiple namespaces in the same file. In this scenario you may then have name collisions after removing the namespace.

Example

Before

export namespace Container {
  export function someCall() { }
  export function anotherCall() { }
}

export namespace AnotherContainer {
  export function someCall() { }
  export function anotherCall() { }
}

After

Renaming Collisions

In this scenario when you remove the namespace you can rename the collisions while maintaining the export like so:

function containerSomeCall() { }
function containerAnotherCall() { }

export const Container = {
  someCall: containerSomeCall,
  anotherCall: containerAnotherCall,
}

function anotherContainerSomeCall() { }
function anotherContainerAnotherCall() { }

export const AnotherContainer = {
  someCall: anotherContainerSomeCall,
  anotherCall: anotherContainerAnotherCall,
}
Decoupling the Code

Another option is to decouple them into their own files. If you want to maintain the exports of the original file though you will need to import and expose them which may seem duplicate, but may be an intermittent step towards a larger refactoring (later updating imports to point at the new files). This also allows you to start writing more modern ESM code too if you would like, while proxying new exports through the old module.

Container.ts

function someCall() { }
function anotherCall() { }

export const Container = {
  someCall,
  anotherCall,
}

AnotherContainer.ts
function someCall() { }
function anotherCall() { }

export const AnotherContainer = {
  someCall,
  anotherCall,
}

OriginalFile.ts
export * from './Container'
export * from './AnotherContainer'

We can proxy the new ESM modules through the old original module.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x