33

This is part ES6 question part React question. I'm trying to use namespaced components in React with ES6 classes and Babel. So I guess the real question is how to name space es6 classes so I can do what is explained here: https://facebook.github.io/react/docs/jsx-in-depth.html#namespaced-components

Since I get an unexpected token error:

class Headline extends Component { ... }

class Headline.Primary extends Component { ...
              ^
Ben
  • 5,283
  • 9
  • 35
  • 44
  • 1
    I think you problably should be working with [modules](http://www.2ality.com/2014/09/es6-modules-final.html) since ES6 does not have the concept of namespaces. – Henrique Barcelos Aug 26 '15 at 15:42

3 Answers3

39

The ECMAScript-6 class declaration syntax expects a standard BindingIdentifer as the class name. A dot is not a valid character inside an identifier name.

In the context used in the link in OP, the "namespace" is an object, and properties are added to that object one by one using the dot notation for property access.

You could replicate that by using a class expression instead:

'use strict'

var ns = {}

ns.MyClass = class {
  constructor() {
    console.log('in constructor')
  }
}

new ns.MyClass()
Amit
  • 45,440
  • 9
  • 78
  • 110
  • 7
    Yeees, yeeeesss, this is what I was looking for! Didn't know you can write like `something.something = class extends something.baseSomething{}` – Mārtiņš Briedis Apr 17 '16 at 15:06
  • 3
    This approach with an anonymous class seems to fail when combined with `extends`. :( – faintsignal Feb 28 '17 at 22:38
  • @faintsignal Does it really? It should work fine, as the name in a class expression is optional. – caw Nov 13 '17 at 22:01
  • @caw I really wish I had taken a moment to elaborate and/or include a fiddle when I made that comment, as I cannot recall the specifics. Perhaps some kinks were worked out of the engine(s) since my remark. – faintsignal Nov 13 '17 at 22:42
  • @faintsignal That was my guess as well. Probably some bug that has been fixed since. It works today, and the spec seems to allow for that, too. – caw Nov 13 '17 at 23:25
  • Fantastic answer!! – Matías Cánepa Apr 02 '21 at 01:22
26

This doesn't really change with ES6, you still will have to do an assignment:

Headline.Primary = class Primary extends Component { … };

However, using classes like Headline as namespaces is getting pretty deprecated with ES6 (and has previously been a questionable practice anyway), you should instead leverage the new module system. Export Primary as a named export, and instead of importing the Headline class rather do import * as headlines from ….

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • You can safely change `class Primary` to just `class` and make it an *anonymous* class expression. (Otherwise, that name `Primary` (without the namespace prefix) would be available *within* that class in addition to the prefixed name.) – caw Nov 13 '17 at 22:00
  • @caw No, named `class` expressions don't make the identifier available in their scope, unlike named `function` expressions. It's just making the name explicit (and, in some cases, necessary to give the constructor its `.name` property) – Bergi Nov 14 '17 at 11:25
  • @Bergi They do! Both functions and ES6 classes behave exactly the same in that regard. What you’re referring to when you say that it’s making the name “explicit” is that function (and class) names can be automatically *inferred* since ES6, which means the name for the function (or class) expression is often superfluous. But still, the function (or class) expression could have a name *different* than the variable or property it is assigned to, in which case the expression’s name will be available *within* the scope of that expression *additionally*. – caw Nov 14 '17 at 18:37
  • @caw Oh, you're right! I did not know about [classScope](http://www.ecma-international.org/ecma-262/6.0/#sec-runtime-semantics-classdefinitionevaluation), thanks. – Bergi Nov 14 '17 at 18:42
17

This link also relates to this question.

In the Module objects section, it is described that you can do something like this:

// headline.js file
export {Headline, Primary}
class Headline {}
class Primary {}

// In another module...

import * as Headline from "headline";

let h = new Headline.Headline();
let hp = new Headline.Primary();

It's not exactly what you are trying to do, but is an alternative.

Another way of doing it is almost like @Bergi has already pointed out, but I'm just clarifying it further:

let Headline = class Headline extends Component { }
Headline.Primary = class Primary extends Component { }

export {Headline as default}

// in another module:
import Headline from 'headline';

let headline = new Headline();
let primary = new Headline.Primary();
Henrique Barcelos
  • 7,670
  • 1
  • 41
  • 66