2

I have a file with the following code:

class Animal {
    doSomething = () => {
        return 'Hi.';
    };
}

class Dog extends Animal {
    doSomething = () => {
        return super.doSomething() + ' Woof!';
    };
}

console.log(new Dog().doSomething());

Note: trying to run the snippet above probably won't work because I can't figure out how to make it use my Babal settings.

Anyway, when I compile it using Babel and run it in Node, I get the following error:

/Users/elias/src/classFieldTest/build/classFieldTest.js:15
            return super.doSomething() + ' Woof!';
                         ^

TypeError: (intermediate value).doSomething is not a function
    at Dog.doSomething (/Users/elias/src/classFieldTest/build/classFieldTest.js:15:26)
    at Object.<anonymous> (/Users/elias/src/classFieldTest/build/classFieldTest.js:21:23)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)
    at bootstrap_node.js:609:3

I am using Babel 6.26.0 with the stage-2 preset, and Node 8.11.1. I can show the commands I'm using if anyone cares.

Why is this happening? I am guessing that super can't be used to access a class field, but what am I supposed to do about this? If I change the doSomething method of Animal to a traditional method (doSomething() { return 'Hi.'; }), it works, but I would rather avoid traditional methods, with the way they redefine this and all the confusion it causes.

Is there any way to access a class field of a superclass?

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
Elias Zamaria
  • 80,938
  • 29
  • 103
  • 136
  • 1
    [Arrow Functions in Class Properties Might Not Be As Great As We Think](https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1) - use proper methods instead! – Bergi Oct 15 '18 at 18:47
  • 2
    What was that downvote for??? – Elias Zamaria Oct 15 '18 at 18:48
  • "*with the way they redefine `this` and all the confusion it causes*" - understanding the dynamic nature of `this` should be the first step before using `class` syntax. Don't step up to arrow functions yet, if the ´this` keyword is still causing confusion. – Bergi Oct 15 '18 at 18:49
  • "Don't step up to arrow functions yet, if the ´this` keyword is still causing confusion". Why? One major advantage of arrow functions is that they don't redefine `this`. In my experience, calling a method on the object it was defined on is what people usually want to do, and calling it on another object is only rarely desired. I know how `this`-rebinding works, but it is still annoying to get errors when passing methods to `setTimeout` (for example), and that can cause confusion in less experienced programmers. – Elias Zamaria Oct 15 '18 at 19:00
  • 1
    Methods are often defined on a prototype object and then called on an object that inherits from it - that's the rule rather than the exception. Also, `super` itself works that way - you want to call a method defined in the superclass on your subclass instance! Instead of trying to avoid `this`, you should educate less experienced programmers. – Bergi Oct 15 '18 at 19:03
  • Correct me if I'm wrong, but I believe arrow functions exist on the instance of the class, while methods live in the prototype – Andrew Oct 15 '18 at 19:05
  • This sort of composition is prime for functional style programming. – evolutionxbox Oct 15 '18 at 19:31
  • There are all kinds of problems here. Among them is, as mentioned, using an arrow function in a class definition. (Another is the decision to add class fields to JS classes, which was a poor decision!) The biggest question I have, though, is why you want to have a function that doesn't use `this` as a member of an object, rather than as a static class method or a normal function. A useful `this` value is surely the main point of an object function. – lonesomeday Oct 15 '18 at 21:21

1 Answers1

3

Why is this happening? I am guessing that super can't be used to access a class field

Yes. Class fields are instance properties, but super tries to access properties on the superclass' prototype object. Your Animal class simply doesn't have a doSomething method - instead, every Animal object has a property that contains a bound function.

but what am I supposed to do about this? If I change it to a traditional method, it works

Yes, you are supposed to do exactly that. This is how methods and super work.

Avoid arrow functions when you don't need them, and especially when they don't work. Also have a look at Arrow Functions in Class Properties Might Not Be As Great As We Think.

Is there any way to access a class field of a superclass?

Yes - it is an instance property, and you can access it in your constructor before overwriting it:

class Animal {
    constructor() {
        this.doSomething = () => {
             return 'Hi.';
        };
    }
}

class Dog extends Animal {
    constructor() {
        super();
        const superDoSomething = this.doSomething;
        this.doSomething = () => {
            return superDoSomething() + ' Woof!';
        };
    }
}

Alternatively, with the class fields proposal and no explicit constructor:

class Animal {
    doSomething = () => {
        return 'Hi.';
    }
}

class Dog extends Animal {
    doSomething = (superDoSomething => () => {
        return superDoSomething() + ' Woof!';
    })(this.doSomething)
}
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • Thanks. I tried your `superDoSomething` idea. It is a bit strange but it works and it is the best thing I've been able to come up with so far. – Elias Zamaria Oct 15 '18 at 20:46
  • Of course it's strange, but it's the only thing you can do if you insist on using arrow functions as instance properties... – Bergi Oct 15 '18 at 20:47