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

Proposal: Derived class constructors and new.target #4609

Closed
objectkit opened this issue Jul 10, 2017 · 5 comments
Closed

Proposal: Derived class constructors and new.target #4609

objectkit opened this issue Jul 10, 2017 · 5 comments

Comments

@objectkit
Copy link

objectkit commented Jul 10, 2017

There are times when you may need to access static class members before super has been called:

/* contrived example */
class Abstract {

    static checkArgs(args) {
        if (0 === args.length) {
            throw new Error('BadArgs')
        }    
    }

    constructor (...args) {
        this.args = args
    }
}

class Concrete extends Abstract {
    constructor(...args) {
        // Cannot access uninitialized variable 'this'
        // this.constructor.checkArgs(args)
        // so use new.target instead
        new.target.checkArgs(args)
        super(...args)
    }
}

class Dense extends Concrete {
    static checkArgs () {
        // no-op
    }
}

try {
    new Concrete()
}
catch (thrown) {
    // 'BadArgs' as expected
}


new Dense() // ok as expected

In CoffeeScript2, the Abstract and Dense class implementations are fine

class Abstract

    @checkArgs: (args) ->
        if 0 is args.length
            throw new Error("BadArgs")

    constructor: (args...) ->
        this.args = args

But none of the following implementations of Concrete are possible as new.target is not available:

class Concrete

    constructor: (args...) ->
        ###
        error: unexpected .
                new.target.checkArgs(args)
                   ^
        ###
        new.target.checkArgs(args)
        super(args...)
class Concrete

    constructor: (args...) ->
        # error: Can't reference 'this' before calling super in derived class constructors
        @constructor.checkArgs(args)
        super(args...)

The result is that Concrete must reference itself to access checkArgs, but this prevents subclasses from override the static checkArgs method.

class Concrete

    constructor: (args...) ->
        # subclasses can no longer override checkArgs
        Concrete.checkArgs(args)
        super(args...)
class Dense extends Concrete
    @checkArgs: -> # no -op

new Dense() # throws "BadArgs" via Concretes reference to Concrete.checkArgs

If I'm not missing something, this looks like a bit of problem to me. How is it possible to reference the constructor and its properties before super in CS2?

@objectkit objectkit changed the title Derived class constructors and new.target CS2: Derived class constructors and new.target Jul 10, 2017
@connec
Copy link
Collaborator

connec commented Jul 10, 2017

How is it possible to reference the constructor and its properties before super in CS2?

It's currently not (except explicitly as Concrete, which is no use in your case), but support for new.target should probably be added (it was mentioned in the original ES2015 classes issue, but slipped off everyone's radar).

As you say, it's a contrived example to demonstrate the issue, but just to elaborate: the example can be made to work trivially by moving checkArgs into the base constructor (where it is defined):

class Abstract
    @checkArgs = ->
        throw new Error('BadArgs') if arguments.length is 0

    constructor: (...args) ->
        this.constructor.checkArgs(args)
        this.args = args

I suppose this case would become 'non-trivial' if the base class was from a library or something, and you wanted to defend it from invalid calls for whatever reason.

@objectkit
Copy link
Author

Thanks for your insight and confirmation @connec 👍

You are right- in the case that if it were mandatory that the static method was to be called, you would define the reference to the static method in the base classes constructor, and subclasses could override the static method later. But if the static method was designed as an optional support method call, which is a valid use case, yes, you are right. One would be stumped without the new.target reference in CS2. In a way, a workaround like you suggested would mean defining an API by constraints of the language which is not unheard of, but too great either.

The 'new.target` reference is unusual looking in ECMAScript as it is. Its difficult to see how it could be added to CS semantics unless the word 'new' was also to have another application? Then again, new.target is undefined anywhere else but in the constructor to the best of my knowledge. Perhaps it should just be a linguistic import into CS2.

Thanks for the link to the discussion/issue and referencing this there btw. Hope the matter pings back on the radar.

@connec
Copy link
Collaborator

connec commented Jul 10, 2017 via email

@objectkit
Copy link
Author

Go forth and poke good sir; new.target beckons!

@GeoffreyBooth GeoffreyBooth changed the title CS2: Derived class constructors and new.target [CS2] Derived class constructors and new.target Jul 13, 2017
@GeoffreyBooth GeoffreyBooth changed the title [CS2] Derived class constructors and new.target [CS2] Proposal: Derived class constructors and new.target Aug 5, 2017
@GeoffreyBooth GeoffreyBooth changed the title [CS2] Proposal: Derived class constructors and new.target Proposal: Derived class constructors and new.target Sep 8, 2017
@GeoffreyBooth
Copy link
Collaborator

It shouldn’t be that hard to at least update the compiler so that new.target compiles.

GeoffreyBooth pushed a commit that referenced this issue Sep 16, 2018
* support new.target

* check token type
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants