69

I have been experimenting with ES6 classes and am wondering if you can change class names dynamically? For example

class [Some dynamic name] {}; 
simon-p-r
  • 3,623
  • 2
  • 20
  • 35
  • 1
    Why would you want a dynamic name for a class? What would be the point? – CodingIntrigue Nov 09 '15 at 09:23
  • I am creating models which I don't want to hard code, the class key word sets the "this.constructor.name" property which is immutable. If I could create dynamically it will set this property correctly for me. Using eval doesn't appear to be the right suggestion at all. – simon-p-r Nov 09 '15 at 09:59
  • 1
    That says it all really. The *instance* should be dynamic, not your class template, but if you're convinced this is the correct approach you can just create a standard function [using the `Function` constructor](http://stackoverflow.com/questions/5905492/dynamic-function-name-in-javascript) and use the prototype as you would have in ES5. Or use a task running like Gulp/Grunt to generate the classes pre-build – CodingIntrigue Nov 09 '15 at 10:12
  • @simon-p-r: `constructor.name` is not immutable. – Bergi Nov 09 '15 at 15:26
  • You also will want to have a look at [Is there any non-eval way to create a function with a runtime-determined name?](http://stackoverflow.com/q/9479046/1048572) (which works for `class`es just as well) – Bergi Nov 09 '15 at 15:34
  • 1
    If using ES5 classes, then `let tmp = { [name](){} };` and to access that named function `o[name]`. Would be cool if we could do that with classes: `let o = { class [name] {} }`. – trusktr Jul 14 '16 at 02:28
  • @Bergi It is read only. – Petr Peller Aug 15 '16 at 22:14
  • @PetrPeller: …but configurable. You can overwrite it if you want. – Bergi Aug 15 '16 at 23:42
  • @Bergi Can you then elaborate how to do it with a class? – Petr Peller Aug 17 '16 at 09:22
  • Just `Object.defineProperty(constructor, "name", {value:…})`. Also, you can add a `static name() { … }` method or getter to the class itself. – Bergi Aug 17 '16 at 09:33
  • One use case is creating things that wrap/extend other classes like higher order components. It's much more useful to have the underlying class name (or some variant) exposed than the thing that altered it. – Jamie Treworgy Sep 28 '16 at 18:08
  • 1
    @CodingIntrigue I came here looking for something (read dynamic class creation) but your comment " use a task running like Gulp/Grunt to generate the classes " is sending me on a different direction which solves a lot of problem which I hadn't thought off. Thanks ...... – Ananda Apr 08 '17 at 15:34
  • 9
    *That says it all really. The instance should be dynamic, not your class template*. I highly disagree. Coming from Java, which is highly static, I appreciate JS being dynamic more every day. I want to set a class name dynamically so my class won't be anonymous. And I want to create classes dynamically because I am working on a library to create and use mixins to achieve multiple inheritance with ES6 classes. The longer I use classical inheritance, the more I have been getting convinced that single inheritance doesn't cut it. JS is **fantastic** *because* it is dynamic, not despite of it. – Stijn de Witt Apr 11 '17 at 21:14
  • 1
    @StijndeWitt Well said. I landed here because I'm trying to do the same. I've been experimenting with class-factory mixins, and also a tool like `class Foo extends multiple(One, Two, Three) {...}` that combines the classes together using Proxy. The real reason I want dynamic class naming is because I can then take a user class spec and generate a class based on a given name, and provide API like `Class('Foo').extends(One, Two, { ... }) which automatically generates a class that duals as class and class-factory mixin. We could also transpile normal `class Foo extends (One, Two, Three) {}` – trusktr Oct 02 '17 at 05:20
  • Etc. There's too many things we could do with dynamic features (but if only super were dynamic instead of static). – trusktr Oct 02 '17 at 05:20
  • @trusktr Please check out my library, [mics](https://github.com/download/mics) and let me know if it could be suitable for your purposes. – Stijn de Witt Oct 02 '17 at 10:14
  • @trusktr We're looking for more collaborators and it would be great if we could invent one great wheel together :) – Stijn de Witt Oct 02 '17 at 10:19
  • @StijndeWitt Responded in the GitHub repo. :) – trusktr Oct 10 '17 at 20:30
  • im also looking for the answer to this one, i came to this realization when doing SOLID principles, where there can be some sort of service provider, and it needs to do the strategy pattern to figure out what type of interface type to instanciate. Example: interface Type1 { ... } class One implements Type1 {...} class Two implements Type1 {...} someServiceProvider(classType: string): Type1 { return new classType(); // where classType could be anything that is of type Type1 } – Frederick G. Sandalo Jul 18 '20 at 00:08

5 Answers5

68
let C = class
{ // ...
}
Object.defineProperty (C, 'name', {value: 'TheName'});

// test: 
let itsName =  (new C()).constructor.name;
// itsName === 'TheName' -> true
Panu Logic
  • 2,193
  • 1
  • 17
  • 21
  • 6
    Hello, it would be helpful if you provided an explanation of your code. – tima Sep 09 '17 at 15:40
  • I believe this is the correct answer. _Note_: `Object.defineProperty(C, 'name', ...)` works while `C.name = ...` throws `Script Error: "name" is read-only` (at least in Firefox). – Wiktor Tomczak Feb 21 '18 at 16:42
  • This should be the correct answer, the accepted answer class does not have a name. – Marcos Casagrande Apr 06 '18 at 12:28
  • 1
    Yes this is the correct answer as of the current date. There is no way of doing this in the class definition itself – Hoffmann Sep 04 '18 at 12:36
  • Is `Object.defineProperty()` supposed to allow modification of read-only properties, or is that just an implementation quirk that's not supposed to be there? – David Given Mar 20 '19 at 22:22
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty – Panu Logic Mar 26 '19 at 01:17
30

There is a pretty simple way to do it:

const nameIt = (name, cls) => ({[name] : class extends cls {}})[name];

Here's the demo.

It uses an object literal to define a field with a desired name that would hold a new class. This causes the new class to automatically get the desired name. After we're done with that, we extract that new class and return it.

Note the parens around the object literal, so that curly braces don't get mistaken for a code block (...) => {...}.

Of course, putting an existing class into named fields won't change the class, so this only works if you are creating a new class. If you only need a dynamic name in one place where you define the class you are naming, you can drop an extra inheritance and just go:

const myClass = {[name]: class {
    ...
}}[name];
Daerdemandt
  • 2,281
  • 18
  • 19
  • could we add method also?? and add new propherty again?? – Zum Dummi Mar 12 '19 at 03:38
  • This technique won't work if we have a static method referring the class as "this". Example : `static create = (args) => new this(args);` In such case the name will be "_class" – pPanda_beta Oct 26 '20 at 16:12
24

There is probably a better solution for whatever you are trying to achieve, but you can assign a class expression to an object:

let classes = {};
classes[someName] = class { ... };

This didn't really change in ES2015: if you want to create a dynamically named binding, you have to use an object or some other mapping instead.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Actually, what changed with ES6, is that the classes' `.name` is now `someName`. – Bergi Nov 09 '15 at 15:28
  • 1
    Or wait, that was only when doing `{[someName]: class {…}}` – Bergi Nov 09 '15 at 15:35
  • 1
    How to add decorators to such class expression? – Jaanus Varus Apr 07 '16 at 12:17
  • @FabianZeindl: there is no reason this shouldn't work, since this standard paractice even before ES6 (except for the `class` value of course). – Felix Kling May 14 '16 at 15:13
  • I checked again, I have a custom babel-transform which, by accident disables this. – Fabian Zeindl May 15 '16 at 11:42
  • @FabianZeindl: Can you tell me which babel-transform this was? The given solution is not working for me, but I do have a bunch of babel transform plugins active. – mhelvens Aug 03 '16 at 01:03
  • @mhelvens I don't remember, but i'm pretty sure it was either (http://npmjs.com/package/babel-plugin-check-data-access) or something internal, I've written myself. – Fabian Zeindl Aug 03 '16 at 06:26
  • @Bergi But is that spec'd? Or just something engines *might* do (name classes based on variable assignement or property assignment)? – trusktr Oct 02 '17 at 05:22
  • @trusktr Yes, that is specified in ES6. – Bergi Oct 02 '17 at 08:09
  • @Bergi I'm talking about `var Foo = class {}`. In Chrome, the class doesn't get named. :( – trusktr Oct 10 '17 at 20:32
  • @trusktr Then that's a Chrome bug, per the spec this should get a name. But you probably should write `class Foo {}` anyway. – Bergi Oct 10 '17 at 20:47
  • @Bergi What about `let Foo; Foo = class {};`? Should that work too? Or does it have to be during the declaration? – trusktr Oct 10 '17 at 20:57
  • @trusktr Yes, that would work as well. All what matters is that the right hand side is an expression for an anonymous function (but not just anything that returns one), and that the left hand side has a definite identifier. – Bergi Oct 10 '17 at 21:01
  • This is incorrect as : `class base {}; var classes = {}; classes.test = class extends base{}; classes.test.name` should return 'test' but it returns 'base' – Anubhav Gupta Aug 02 '18 at 14:05
  • @AnubhavGupta: Not sure whether you are referring to my answer or a comment. The `name` of the function is not of concern in this answer, only *how to refer to it* in code. I never made the claim that the `name` property has a specific value. – Felix Kling Aug 02 '18 at 14:24
4

To take it a bit further playing with dynamic class names and dynamic inheritance, when using babel you can just do something like this:

    function withname(name, _parent) {
        return class MyDinamicallyNamedClass extends (_parent||Object) {
            static get name() { return name || _parent.name }
        }
    }
  • 4
    But console still output `class MyDinamicallyNamedClass ...` – trusktr Oct 02 '17 at 05:24
  • Yeah but I think the point he is making is that       ( `MyDinamicallyNamedClass instanceof _parent`) returns true \n       (btw thats not how you spell Dynamically, lOlz) – JΛYDΞV May 22 '21 at 07:45
4

One way, even if not ideal, is simple with eval:

~function() {
    const name = "Lorem"

    eval(`
        var ${name} = class ${name} {} 
    `)

    console.log(Lorem) // class Lorem {}
}()

Note, it has to be with var. Using let, const, and plain class inside the eval won't work.

Another way with Function:

~function() {
    const name = "Lorem"

    const c = new Function(`
        return class ${name} {}
    `)()

    console.log(c) // class Lorem {}
}()

Sitenote: you can pass scope variables into the Function and use them inside:

~function() {
    const name = "Lorem"
    const val = "foo"

    const Class = new Function('val', `
        return class ${name} {
            constructor() {
                console.log( val )
            }
        }
    `)( val )

    console.log(Class) // class Lorem {}
    new Class // "foo"
}()
trusktr
  • 44,284
  • 53
  • 191
  • 263
  • 1
    Note that using `Function` this way is also `eval` and carries the same risks. – eyelidlessness May 12 '18 at 19:01
  • 3
    That's only a problem if your sticking 3rd-party code inside the `Function` or `eval`. It isn't a problem if you own the code your sticking in there (f.e. the code string is generated in the same scope as the `Function` or `eval`, with no input from the outside). – trusktr May 16 '18 at 20:19
  • @eyelidlessness That's only a problem if your sticking 3rd-party code inside the `Function` or `eval`. It isn't a problem if you own the code your sticking in there (f.e. the code string is generated in the same scope as the `Function` or `eval`, with no input from the outside). As you can see in my example, the strings are generated in-place, which is completely safe (and assuming my code is inside a module, it is impossible for variables to be modified from the outside). – trusktr May 16 '18 at 20:20
  • I've updated my examples to use closures (much like modules). – trusktr May 16 '18 at 20:21
  • 2
    It's still worth pointing out because people tend to take code examples and expand on them. It may not be immediately obvious to someone less familiar with how this works that it uses `eval`. I strongly encourage you to add a warning to that effect. – eyelidlessness May 17 '18 at 19:08