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
Comments
Hey @xuyuanxiang! We really appreciate you taking the time to report an issue. The collaborators If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack |
They are legacy. Use es modules. |
Namespaces imply one of two things:
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. |
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. |
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.
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. |
@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. |
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. |
@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. |
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 :) |
@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. |
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. |
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 ? |
@chawax |
@afshin-hoseini I found a solution to use |
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 = ... |
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 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). |
@sonhanguyen @martpie : some info: Via @DanielRosenwasser - facebook/create-react-app#4837 (comment) :
via @Timer - facebook/create-react-app#5681 (comment) :
I also got hit by this on OpenAPITools/openapi-generator#1947 , but going forward namespaces isn't going to stay in TypeScript anyway. |
We use Swagger codegen as well and this is what is preventing us from moving to babel |
In case anyone finds this helpful: |
Big area without any possible workaround is custom JSX factory: https://www.typescriptlang.org/docs/handbook/jsx.html You are supposed to use namespaces:
Our project uses this and I didn't find any workaround. |
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 |
@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. |
@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... |
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. |
@Wolvereness any suggestions on the best way to test your PR? I tried to Do I just need to build from source & |
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. |
@Wolvereness I cloned down your branch, ran Linked it in my webpack/typescript/react project which is using |
For those still looking for other ways to use namespaces with Babel and Typescript, a workaround is to make an |
If you've been following this issue, namespace support has been merged in. |
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.
* 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.
@jaredpalmer edit: just realized that NS support is coming, so having an alternative codegen is probably not required any more |
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.
The text was updated successfully, but these errors were encountered: