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

Typescript namespaces syntax should be supported #8244

Closed
xuyuanxiang opened this issue Jul 1, 2018 · 30 comments
Closed

Typescript namespaces syntax should be supported #8244

xuyuanxiang opened this issue Jul 1, 2018 · 30 comments
Labels
area: typescript outdated A closed issue/PR that is archived due to age. Recommended to make a new issue

Comments

@xuyuanxiang
Copy link

Namespaces syntax should be supported

I’m trying to begin using Babel 7.x to compile my project, But failed and got the following error message:SyntaxError: Namespaces are not supported.

@babel-bot
Copy link
Collaborator

Hey @xuyuanxiang! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.

@xuyuanxiang xuyuanxiang changed the title Namespaces syntax should be supported Typescript namespaces syntax should be supported Jul 1, 2018
@TrySound
Copy link
Contributor

TrySound commented Jul 1, 2018

They are legacy. Use es modules.

@DanielRosenwasser
Copy link
Member

Namespaces imply one of two things:

  1. Global code structuring (as they'll merge across files)
  2. Extra layers of nesting within a module

Neither case seems to be ideal, nor does it feel like the direction the JavaScript community is taking. But more than that, there's a technical constraint which is that Babel's single-file-at-a-time model doesn't really support cross-file resolution.

@hzoo
Copy link
Member

hzoo commented Jul 26, 2018

Maybe we need a clearer error message to say that we are not implementing support for namespaces (at least don't see a good enough reason to)?

This is also documented here: https://babeljs.io/docs/en/next/babel-plugin-transform-typescript.html#caveats.

@hzoo hzoo closed this as completed Jul 26, 2018
@VictorQueiroz
Copy link

Isn't there a workaround for this? I mean, there's still projects there using namespaces and I could find no data about "namespaces legacy", it seems to still be perfectly encouraged in TS.

A note about terminology: It’s important to note that in TypeScript 1.5, the nomenclature has changed. “Internal modules” are now “namespaces”. “External modules” are now simply “modules”, as to align with ECMAScript 2015’s terminology, (namely that module X { is equivalent to the now-preferred namespace X {).

It's sad because I want to use Babel for transpilation only and TS for type checking and it's impossible because the project is attached to namespaces and babel plugin for supporting TS doesn't support a native feature of TS.

@TrySound
Copy link
Contributor

@VictorQueiroz The problem is that babel doesn't have type information to compile namespaces. Same with enums. If you can't migrate just use typescript compiler.

@martpie
Copy link

martpie commented Aug 28, 2018

This is an annoying feature with Next.js especially.

// interfaces.ts
export namespace Something {
  export interface Products {
    ...
  }

  export interface Selection {
    ...
  }

  export interface State {
    selection: Selection;
    products: Products;
  }
}

export namespace Another {
  export interface Products {
    ...
  }

  export interface Selection {
    ...
  }

  export interface State {
    selection: Selection;
    products: Products;
  }
}

// ...

I am not sure how to mitigate that. It is more of a Next.js issues ofc, but the thing is a lot of tools/framework are trying to do everything via Babel today. And the frustration is real when you hit limitations like that.

@VictorQueiroz
Copy link

VictorQueiroz commented Aug 28, 2018

@KeitIG Although I think babel should cover that somehow (I can even generate TypeScript code using Babel, but can't parse TypeScript code), I've come to found a solution for just generating JavaScript code directly from TypeScript code, that might help you:

const ts = require('typescript');
ts.transpileModule(code, {
  target: 'esnext'
}).outputText

That could be a workaround meanwhile there's no better solution using Babel.

@maciejw
Copy link

maciejw commented Sep 10, 2018

namespaces in typescript are quite useful, consider this code:

export class MyComponent extends React.Component<MyComponent.Props> {
  render() {
    return <div>{this.props.prop1}</div>;
  }
}
export namespace MyComponent {
  export type Props = { prop1: string };
}

I can import my component and types associated with it, using one statement

import { MyComponent } from '.....'

babel can ignore this namespace declaration because this is type declaration only, this warning you display, you could show when someone tries to export const from namespace, or declares a class or a function. consts in namespace have its counterpart in es, witch are static properties.

I understand your argument, you are making, just put props as module export, but what if I have 2 components in one module? then I have to name my props like MyComponentProps, witch is no bueno :)

@martpie
Copy link

martpie commented Sep 10, 2018

@maciejw From what I understood so far, it is "technically" not possible to implement them with Babel. This is a pain with tools like Next.js which goes "full Babel".

If you can, use Webpack to transpile TypeScript, not Babel. And anyway, most Babel's features are in TypeScript compiler.

@maciejw
Copy link

maciejw commented Sep 10, 2018

no, I do not want to implement namespaces in babel, I want to be able to get rid of them safely with babel, like any other type annotation and maybe show some warnings, not errors

let x: string | undefined;

you support it because it is perfectly legal js code.

let x;

the same is with namespace in some cases.

export namespace MyComponent{
  export type Type1 = {foo: string}
}

is legal in js, because you can remove hole namespace, but you throw an exception instead

when I declare it like this

export declare namespace MyComponent{
  export type Type1 = {foo: string}
}

babel will NOT complain. witch is some kind of workaround for me and many others I presume.

but what would be ideal is the following:

you could throw errors only when we have something with implementation in namespace

export namespace MyComponent{
  export type Type1 = {foo: string}
  export interface Type2 { foo: string }
  export abstract class C1 { x(); }
}

is OK

export namespace MyComponent{
  export const someData = {foo: 'value'}
  export function f(){}
  export class C1 { x(){} }
}

is NOT OK all the time, because of implementation in ambient context.

export namespace MyComponent{
  export class C1 {}
  export const index = 5
  export const val = 'val'
}

is OK some times, it depends on usage. this is valid usage

const val: typeof MyComponent.val = 'val'
const index: typeof MyComponent.index = 5
class C2 implements MyComponent.C1 {}

this is NOT OK

const val = MyComponent.val
const index = MyComponent.index
class C2 extends MyComponent.C1 {}

in TS when I declare namespace I can only put types, and use them as types

declare namespace MyComponent {
  export type Props1 = { prop1: string }; // valid
  export interface Props2 { prop1: string; } // valid
  export class C1 {} //valid, possible runtime error
  export const prop1 = 0; //valid, possible runtime error
  export const prop2 = 'aaa'; //valid, possible runtime error
  export const prop3 = { bla: 1 }; //compilation error
  export function bla() {} //compilation error
  export class C2 { x(){} } //compilation error
}

but event TS will not prevent me from generating runtime error

class C2 extends MyComponent.C1 { } //it is runtime error, it compiles fine.

I thing it would be cool to show some warning when I put classes or consts with string and number literals in namespace and show errors for function and any other literals.

TS compiler could do the same thing on its side and make things compatible with your behaviour.
There are issues with namespaces is typescript after transpiration to js, this could fix some problems there also microsoft/TypeScript#21327 maybe namespaces shoud be use only for type declarations? @andy-ms what do you think?

@chawax
Copy link

chawax commented Nov 12, 2018

The fact that Babel 7 doesn't support Typescript namespaces is very annoying for me. In my React Native project I use Swagger code generators to generate my REST client layer. But all the generators I found use namespaces (and also enums that are not supported neither by Babel 7 if I understood well). Changing the templates is too much complicated and time consuming, so this is not an option for me. So what is the solution ? It worked well with previous releases of RN, but it fails with RN 0.57. Is there a way to tell Babel 7 not compiling Typescript ? So that it could work as it was the case with RN < 0.57 ?

@afshin-hoseini
Copy link

@chawax
Please let me know what have you done with your problem?

@chawax
Copy link

chawax commented Dec 18, 2018

@afshin-hoseini I found a solution to use react-native-typescript-transformer instead of Babel 7 to compile Typescript.

facebook/react-native#21074 (comment)

@sonhanguyen
Copy link

sonhanguyen commented Jan 9, 2019

May I ask what was the reason for not having namespaces? Is it like "This feature is bad so we want to exclude it" or "There is some complexity that isn't worth prioritising, but contribution is welcome".

I'm not sure I agree with the position in which tool A "supports" tool B but leave out some features. There are legit reasons for it at times, like complexity as mentioned. However generally it's not tool A who should make the call of some feature in tool B being deprecated. If it is the case tool B should have already thrown warning and people should have already stopped using it. Even then, legacy code should be taken into account.

In the context of namespaces, they are widely used in typescript codebases. Global modules are discouraged and automatically turned off when you have any import or export statement. However that's a separate thing from namespaces even though they use the same keyword, and I think this is where the confusion comes from. Personally I find it useful for grouping code and type, which should always be exported with the object/function because there is no cost:

const Modal = () => <div />
namespace Modal {
   export type Props = ...
   export const defaultProps = ...
   export const Cancellable = ...
   export const Header = ...
   export const Footer = ...
}
export default Modal
// I guess you can make some argument like "default export is bad" but please, leave that decision to the programmers
// realistically there is no need to split the code of a modal dialog from that of the footer

The alternative which is smulf naming is pretty repetitive, not as consistent and not as discoverable (you have to go back to the top of the file and import a type for one)

export const Modal = () => <div />
Modal.defaultProps = ...
export type ModalProps = ...
export const ModalHeader = ...
ModalHeader.defaultProps = ...
export type ModalHeaderProps = ...
export const ModalFooter = ...
ModalFooter.defaultProps = ...
export type ModalFooterProps = ...

@martpie
Copy link

martpie commented Jan 9, 2019

From what I understood so far, the problem is not that "this feature is legacy", but more "it is not technically feasible with Babel".

The following needs a confirmation, but @babel/preset-typescript is nothing like ts-loader: it does not do any typechecking, it just strip/transpile the code to a JS-friendly syntax. Like you know, just stripping the type annotations etc, it does not use tsc under the hood.

That sucks, and we have to blame the "all-use-babel" (css-in-js libs, some css modules helpers for react...) spirit of the community, but there's not much we can expect right now. If you can, go full Webpack + ts-loader and don't use Babel.

refs:

edit: you can downvote things as much as you want, it's not about being good or bad, but about being wrong or right. This feature CANNOT be implement in Babel (I did not say "won't", I said "cannot"). And we have to deal with it (and I am the first pissed off).

@ceefour
Copy link

ceefour commented Jan 20, 2019

@sonhanguyen @martpie : some info:

Via @DanielRosenwasser - facebook/create-react-app#4837 (comment) :

TypeScript PM here. Broadly speaking, I feel that

namespaces are not a huge loss; the React community is firmly on-board with ES modules anyway, and that's the direction the TypeScript world is taking.
const enums are often misused. Sure, they can be great for the internals of a library to get some speed improvements, but they generally shouldn't be used outside of that scope.
Merging behavior is marginally useful now that TypeScript 3.1 has the ability to tack properties onto functions. The fact that enums merge is also something I've never seen used out in the wild.

via @Timer - facebook/create-react-app#5681 (comment) :

I'm sorry and understand that this is likely frustrating, but namespaces are a proprietary, legacy feature. They have been replaced by specification behavior: ES Modules.
Namespaces are deprecated. The TypeScript documentation is going to remove all references to them in examples and stop usage. In other words, you should not be using namespaces anymore.
Const Enums are rarely needed and often misused.

I also got hit by this on OpenAPITools/openapi-generator#1947 , but going forward namespaces isn't going to stay in TypeScript anyway.

@jaredpalmer
Copy link

The fact that Babel 7 doesn't support Typescript namespaces is very annoying for me. In my React Native project I use Swagger code generators to generate my REST client layer. But all the generators I found use namespaces (and also enums that are not supported neither by Babel 7 if I understood well). Changing the templates is too much complicated and time consuming, so this is not an option for me. So what is the solution ? It worked well with previous releases of RN, but it fails with RN 0.57. Is there a way to tell Babel 7 not compiling Typescript ? So that it could work as it was the case with RN < 0.57 ?

We use Swagger codegen as well and this is what is preventing us from moving to babel

@jeysal
Copy link
Contributor

jeysal commented Feb 22, 2019

In case anyone finds this helpful:
While migrating Jest to TypeScript, we've encountered the problem that we need to do export = in some modules (using proper ESM default exports in them would be nice, but a breaking change) but at the same time we also want to export type SomeType from them. This StackOverflow answer explains how to achieve this using TypeScript using namespaces.
Because namespaces are not (and cannot fully be) supported by @babel/plugin-transform-typescript, but we only need them to declare a few types / interfaces inside, I made a Babel plugin to be used in addition to transform-typescript that can strip those simple namespaces away.
On a side note, the export = declarations are transformed to module.exports = by another Babel plugin that we use in Jest. The combination of those two allows us to do a legacy CJS export of a class and export some types from the very same module.

@mikea
Copy link

mikea commented Mar 13, 2019

Big area without any possible workaround is custom JSX factory:

https://www.typescriptlang.org/docs/handbook/jsx.html

You are supposed to use namespaces:

declare namespace JSX {
    interface IntrinsicElements {
        foo: any
    }
}

<foo />; // ok
<bar />; // error

Our project uses this and I didn't find any workaround.

@ChuckJonas
Copy link

ugh... I just finished migrating a large project over to babel7... I didn't realize this was a limitation...

I use namespaces the same way as @maciejw showed above. A great usecase is putting an enum statically on a class:

export class Image
{
    constructor ()
    {
        this.state = Image.State.Idle;
    }

    public state: Image.State;
}

export namespace Image
{
    export enum State
    {
        Idle,
        Loading,
        Ready,
        Error
    }
}

//elsewhere
import {Image} from './image';
let img = new Image()
img.state = Image.State.Error

@milesj
Copy link

milesj commented Apr 17, 2019

@ChuckJonas This is much cleaner IMO.

export enum State {
  Idle,
  Loading,
  Ready,
  Error
}

export class Image {
  state: State = State.Idle;
}

//elsewhere
import {Image, State} from './image';
let img = new Image()
img.state = State.Error

This also aligns with the entire JS community/ecosystem.

@ghost
Copy link

ghost commented Apr 17, 2019

@milesj thats fine for the example above (and how I would probably do it as well), but I have a situation where I'm generating a bunch of classes and each class has the same set of enums. If I were to do it that way I have to either change my code generation to be more verbose (prefix the classname in front of each enum) or rename the enums on import...

@Wolvereness
Copy link
Contributor

I do have an open PR on babel to support namespaces. I encourage y'all to try testing it on your projects and leave feedback.

@ChuckJonas
Copy link

@Wolvereness any suggestions on the best way to test your PR? I tried to npm install @babel/core@7.4.3+pr.9785... which succeeded... but I don't think it actually worked.

Do I just need to build from source & npm link?

@Wolvereness
Copy link
Contributor

Internally we have a patch process to enable it, as we weren't able to get a pre-publish successfully built. A maintainer would probably have better advice on how to properly get a local build for testing target projects.

@ChuckJonas
Copy link

@Wolvereness I cloned down your branch, ran make and then npm link.

Linked it in my webpack/typescript/react project which is using enum inside of namespace and it worked flawlessly. Hopefully it will get released soon!

@rioam2
Copy link

rioam2 commented Jun 25, 2019

For those still looking for other ways to use namespaces with Babel and Typescript, a workaround is to make an index.d.ts file in the same folder as the code you're working on and move the namespace declaration there using declare namespace ..... The babel transpiler ignores this file, but your editor/tsc will still use it for type/namespace definitions. 😉

nicolo-ribaudo pushed a commit that referenced this issue Jun 30, 2019
* Add module tests for typescript namespace transform

Fixes #8244, fixes #10038
@Wolvereness
Copy link
Contributor

If you've been following this issue, namespace support has been merged in.

0a98814

benjamn added a commit to meteor/babel that referenced this issue Jul 6, 2019
Babel's TypeScript implementation has a few unfortunate caveats:
https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats

Most notably, the lack of *full* support for namespaces is painful:
babel/babel#8244
babel/babel#9785

By precompiling TypeScript code with the actual TypeScript compiler, we
can support features like namespaces without relying on Babel. Of course,
Babel still handles everything after TypeScript syntax has been removed.
benjamn added a commit to meteor/babel that referenced this issue Jul 6, 2019
* Use actual TypeScript instead of @babel/preset-typescript.

Babel's TypeScript implementation has a few unfortunate caveats:
https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats

Most notably, the lack of *full* support for namespaces is painful:
babel/babel#8244
babel/babel#9785

By precompiling TypeScript code with the actual TypeScript compiler, we
can support features like namespaces without relying on Babel. Of course,
Babel still handles everything after TypeScript syntax has been removed.

* Test that JSX syntax works in .tsx files.
@danielwinkler
Copy link

danielwinkler commented Jul 17, 2019

@jaredpalmer
See my reply to @chawax in a different thread on the swagger codegen issue
facebook/react-native#21074 (comment)
Let me know if a PR would be of interest

edit: just realized that NS support is coming, so having an alternative codegen is probably not required any more

@lock lock bot added the outdated A closed issue/PR that is archived due to age. Recommended to make a new issue label Nov 4, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Nov 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: typescript outdated A closed issue/PR that is archived due to age. Recommended to make a new issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.