81

I am trying to instantiate new HTMLDivElement in TypeScript

var elem = new HTMLDivElement();

but the browser throws

Uncaught TypeError: Illegal constructor.

The workaround seems to be to use

var elem = document.createElement('div');

but I find this suboptimal for various reasons.

Why can't I instantiate DOM elements directly when there is a new keyword for in in the lib.d.ts?

declare var HTMLDivElement: {
    prototype: HTMLDivElement;
    new (): HTMLDivElement;
}
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
daniel.sedlacek
  • 8,129
  • 9
  • 46
  • 77

3 Answers3

101

Note that the error you get here is "Illegal constructor". This is different from the error "object is not a function" that you would get if you were to write new {}(), for example.

So, technically, HTMLDivElement does have a constructor. It's just that this particular constructor throws an exception rather than creating an object.

Normally lib.d.ts would just exclude such useless signatures, but TypeScript requires that the right-hand side of the instanceof operator have a constructor. In other words, it's legal to write foo instanceof HTMLElement, but not foo instanceof [], and the difference is determined by the fact that HTMLElement has a constructor.

There were basically three choices on the table here:

  1. Remove the construct signature from the DOM elements and remove the restriction on instanceof's right operand. This is undesirable because you'd really prefer to catch errors where someone accidentally writes code like x instanceof foo instead of x instanceof Foo (where foo is an instance of Foo).
  2. Remove the construct signature from DOM elements, but keep the instanceof check in place. This is bad because foo instanceof HTMLElement is a very reasonable thing to write.
  3. The current situation, where the constructors exist but you have to know not to call them.

You can't construct DOM elements using normal constructors because you're supposed to go through document.createElement. This is basically for technical reasons relating to how browsers implement these objects.

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • 3
    Amazing explanation. Would it be possible to have a comment in lib.d.ts explaining this? Or you don't want to pollute lib.d.ts with comments? – daniel.sedlacek Oct 07 '14 at 09:18
  • 8
    Would it make sense to add a warning or error from `tsc` for code that invokes constructors on subclasses of `Element`? – Drew Noakes Oct 08 '14 at 08:15
  • If you extend HTMLElement, for example `class Thing extends HTMLElement` then you CAN use new to create an instance with `let t = new Thing()`. Then you can add it to the DOM! – Kokodoko Jul 18 '17 at 19:56
  • This appears not to be the case, Kokodoko: `class Thing extends HTMLElement {} `; `let t = new Thing()` results in `TypeError: Illegal constructor.` –  Sep 15 '17 at 00:28
  • 1
    `customElements.define( 'custom-tag', Thing );` then call `new Thing()` - which returns `` – flcoder Sep 02 '18 at 06:02
  • notice there are a few exceptions. for example: you can create an `audio` element using it's ctor – Meir Blachman Feb 10 '20 at 14:54
  • @ryan-cavanaugh , why doesn't TypeScript compile to `document.createElement`, instead of producing code which is known to not work? TS is all about trying to make JS useful and behave like a real programming language, right? Not being able to instantiate a class with `new` does not seem very useful, neither intuitive nor productive. What prevents TypeScript from compiling `new HTMLDivElement()` into `document.createElement('div')`? If the class can't be instantiated with `new`, it should be marked as being abstract or something like that, that would at least produce a compile time error. – NoOneSpecial Nov 20 '21 at 09:03
  • TypeScript doesn't change the meaning of existing code. – Ryan Cavanaugh Nov 21 '21 at 22:10
37

You have mistaken typescript syntax with c# syntax.

Just replace

var elem = new HTMLDivElement();

with one of the following

var elem = document.createElement('div');

or

var elem = <HTMLDivElement>(document.createElement('div'));

or

let elem = document.createElement('div') as HTMLDivElement

(if you need to use HTMLDivElement attributes)

Skdy
  • 280
  • 1
  • 5
  • 16
8

If you extend HTMLElement or any subclass like HTMLDivElement

You also need to register the custom element


class SampleDiv extends HTMLDivElement{
    constructor(){
       super();
    }
}
customElements.define('sample-div', SampleDiv, { extends: 'div' });

See more here: https://javascript.info/custom-elements

Joshua Lochner
  • 153
  • 1
  • 9
Nick Mitchell
  • 1,207
  • 13
  • 24