3

I would like to extend the built-in class HTMLElement with additional methods. Maybe I'm going mad, but I thought the following was the official idiom:

interface HTMLElement {
   swapChildBefore(remove: HTMLElement, insert: HTMLElement, before: HTMLElement): void;
}

HTMLElement.prototype.swapChildBefore =
   function (remove: HTMLElement, insert: HTMLElement, before: HTMLElement): void {
      this.removeChild(remove)
      this.insertBefore(insert, before)
   }

At least, according to How does prototype extend on typescript?, something like this should work.

However, this seems to hide all the existing methods on HTMLElement. Is that because I've declared an interface, which hides the class of the same name? But this idiom seems to work fine with Object and Array, which are also classes.

Community
  • 1
  • 1
Roly
  • 2,126
  • 2
  • 20
  • 34
  • 2
    `HTMLElement` already has a `swapChildBefore` method taking two `HTMLElement` as parameters. You should use a different name for the method if you want a variation with three parameters. – MiMo Mar 18 '13 at 15:34
  • Oh, ok. Thanks. (I wasn't able to Google for it, though. The only search result is this StackOverflow question.) – Roly Mar 18 '13 at 15:40

2 Answers2

6

Expanding interfaces has to be done in the root level of the code. If you try to extend an interface inside of a module, code inside of that module will only seen the interface inside of that scope.

Broken Example:

interface ExpandableInterface {
    memberOfFIRSTDefinition: number;
}

module MyModule {
    interface ExpandableInterface {
        memberOfSECONDDefinition: number;
    }

    class MyClass {
        constructor() {
            var m: ExpandableInterface = {};
            m.memberOfFIRSTDefinition; // <-- It can't see this member because it's only scoped to the one inside of the module.
        }
    }
}

Working example:

interface ExpandableInterface {
    memberOfFIRSTDefinition: number;
}

interface ExpandableInterface {
    memberOfSECONDDefinition: number;
}

module MyModule {
    class MyClass {
        constructor() {
            var m: ExpandableInterface = {};
            m.memberOfFIRSTDefinition; // <-- They're both root level, it can be seen :)
        }
    }
}
Joseph Lennox
  • 3,202
  • 1
  • 27
  • 25
  • Thanks. Actually I had it like that initially, and forgot to change it back. Should it make a difference? Anyway, I agree that it's clearer as a regular method signature, so I edited my original question. The problem still remains, though: it seems I'm hiding the HTMLElement class by introducing this interface, which isn't how things seem to work when extending Object or Array. – Roly Mar 18 '13 at 15:36
  • Interesting. The code I pasted did that without issue. Is your interface definition inside of the root code level, and not nested inside a model or the like? I suspect it might be a scope issue. – Joseph Lennox Mar 18 '13 at 16:00
  • Hmm, just realised it's inside a module, and that the problem goes away if I hoist the definition to the top level. I guess this kinda makes sense (I'm still getting used to open interfaces). If you want to post that as answer, I'll accept it. – Roly Mar 18 '13 at 16:52
  • Thanks. I updated my response to properly answer your updated question and our discoveries :) – Joseph Lennox Mar 18 '13 at 23:12
2

It works fine for me - after I declare that additional method (using a different name not to clash with the existing swapChildrenBefore) the TypeScript compiler is happy also with pre-existing properties and methods of HTMLElement. See here

MiMo
  • 11,793
  • 1
  • 33
  • 48