Skip to content

Define that interface objects are constructors #698

Open

Description

@Ms2ger

ES claims the following (twice):

Built-in function objects that are not identified as constructors do not implement the [[Construct]] internal method unless otherwise specified in the description of a particular function.

We don't normatively identify the interface object as a constructor, even if it has a [Constructor]. I guess we should.

A less obvious question is what we should do if there's no [Constructor]. Right now, I think we're aiming to install a [[Construct]] internal method that always throws, but that makes the interface object pass IsConstructor().

Activity

  1. bzbarsky commented on Mar 26, 2019

    @bzbarsky
    Contributor

    unless otherwise specified in the description of a particular function

    This is the key, no? https://heycam.github.io/webidl/#Constructor specifies otherwise:

    If the [Constructor] extended attribute appears on an interface, it indicates that the interface object for this interface will have an [[Construct]] internal method

    So I think we're good here: thing with a [Constructor] have [[Construct]] and things without do not, as desired.

  2. bzbarsky commented on Mar 26, 2019

    @bzbarsky
    Contributor

    Though I guess that leaves open the question of the no-[Constructor] case. I just checked Gecko, and it does have a [[Constructor]] in that case, albeit one that always throws. I wonder whether the output of IsConstructor() is observable, other than by the exceptions that get thrown.

  3. bzbarsky commented on Mar 26, 2019

    @bzbarsky
    Contributor

    I guess you could do something where you use a DOM interface object as the .constructor of an array and see what happens in https://tc39.github.io/ecma262/#sec-arrayspeciescreate step 6.

  4. bzbarsky commented on Mar 26, 2019

    @bzbarsky
    Contributor

    And also whether a proxy for a DOM interface object ends up with a construct trap being called. So simple testcase:

    <script>
      var p = new Proxy(Node, { construct: () => console.log("constructing") });
      new p()
    </script>
    
  5. bzbarsky commented on Mar 26, 2019

    @bzbarsky
    Contributor

    On that testcase, Chrome and Firefox log "constructing"; Safari does not (and the exception it throws is about p not being a constructor). Yay interop. ;)

    But OK, if we want to spec Chrome/Firefox behavior here then we need to change something in the idl spec.

  6. bzbarsky commented on Mar 26, 2019

    @bzbarsky
    Contributor

    And admittedly it would be good if the "is a constructor" bit was near the bit that defines the steps, not way off somewhere else...

  7. domenic commented on Apr 3, 2019

    @domenic
    Member

    I've always assumed they had constructors (and [[Construct]] methods), just throwing ones.

    I think it'd be very strange to have callable interface objects exist at all, and be reachable via the .constructor property, if they are not also [[Construct]]able.

    In practice it doesn't matter much, but I do prefer keeping [[Construct]] and just saying that it throws for things without a constructor in the IDL.

  8. bzbarsky commented on Apr 3, 2019

    @bzbarsky
    Contributor

    There was an interesting question about feature detection that was raised the other day: how to detect a change from "no [Constructor]" to "no-arg [Constructor]". Right now, I think "call the constructor and see if it throws" is the only way in Firefox and Chrome. The Proxy thing above could be used if we changed whether [[Construct]] happens...

    But maybe this is enough of an edge case that it's not an issue.

    @rniwa would Safari be open to changing its behavior here, or do you know whom I should be asking about it?

  9. rniwa commented on Apr 3, 2019

    @rniwa

    What's the benefit of implementing Chrome/Firefox behavior here?

  10. bathos commented on Oct 3, 2019

    @bathos
    Contributor

    FWIW, it seems there’s a number of ways to observe this distinction beyond just Proxy — pretty much anywhere IsConstructor is used, I guess. In addition to peeking out through various programmatic APIs, it can also impact evaluation of ‘bare’ syntax (third example below). Each of the following statements will throw a TypeError if Foo[[Construct]] is absent, but will not throw if it’s present:

    Reflect.construct(Bar, [], Foo);
    
    customElements.define('x-x', Foo);
    
    class Bar extends Foo {}

    Usually it’s throw vs don’t throw, but sometimes potentially neither throws but they still do other different things:

    // If Foo[[Construct]] is present, (attempts) returning new Foo.
    // If Foo[[Construct]] is absent, returns new Array.
    
    Array.from.call(Foo, []);
    
    // In practice here, the first case throws for the specific Web IDL issue under discussion,
    // though it’s the reverse of the other cases (i.e. throw when [[Construct]] _is_ present).

    I think it'd be very strange to have callable interface objects exist at all, and be reachable via the .constructor property, if they are not also [[Construct]]able.

    I agree.

  11. shvaikalesh commented on Oct 20, 2020

    @shvaikalesh
    Contributor

    @rniwa would Safari be open to changing its behavior here, or do you know whom I should be asking about it?

    This behavior was changed in JSDOMConstructorNotConstructable should be a constructor, aligning Safari with Chrome/Firefox.

  12. domenic commented on Oct 20, 2020

    @domenic
    Member

    Thanks @shvaikalesh! Do you know if there are web platform tests for this?

    I looked into how we would update the Web IDL spec to be explicit about this, and it's still kinda unclear to me, largely because the ES spec is unclear. I will open an issue on tc39/ecma262 asking for guidance.

  13. shvaikalesh commented on Oct 20, 2020

    @shvaikalesh
    Contributor

    Do you know if there are web platform tests for this?

    There are tests asserting that (class extends HTML___Element {}) doesn't throw, and that's it.

    idlharness.js has a FIXME for implementing [[Construct]] check.

  14. added a commit that references this issue on Mar 15, 2021
  15. added a commit that references this issue on Apr 26, 2021
  16. deleted a comment from on Mar 21, 2025
  17. deleted a comment from NoMercyTH on Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions