85

In the default TypeScript HTML app from visual studio, I added

HTMLElement 

to the first line of the window.onload event handler, thinking that I could provide a type for "el".

thus:

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;

    constructor (element: HTMLElement) { 
        this.element = element;
        this.element.innerText += "The time is: ";
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.span.innerText = new Date().toUTCString();
    }

    start() {
        this.timerToken = setInterval(() => this.span.innerText = new Date().toUTCString(), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }

}

window.onload = () => {
    HTMLElement el = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};

I get an error

Compile Error. See error list for details .../app.ts (25,17): Expected ';'

Any clue why? I suspect I am missing something obvious.

bnieland
  • 6,047
  • 4
  • 40
  • 66

5 Answers5

97

The type comes after the name in TypeScript, partly because types are optional.

So your line:

HTMLElement el = document.getElementById('content');

Needs to change to:

const el: HTMLElement = document.getElementById('content');

Back in 2013, the type HTMLElement would have been inferred from the return value of getElementById, this is still the case if you aren't using strict null checks (but you ought to be using the strict modes in TypeScript). If you are enforcing strict null checks you will find the return type of getElementById has changed from HTMLElement to HTMLElement | null. The change makes the type more correct, because you don't always find an element.

So when using type mode, you will be encouraged by the compiler to use a type assertion to ensure you found an element. Like this:

const el: HTMLElement | null = document.getElementById('content');

if (el) {
  const definitelyAnElement: HTMLElement = el;
}

I have included the types to demonstrate what happens when you run the code. The interesting bit is that el has the narrower type HTMLElement within the if statement, due to you eliminating the possibility of it being null.

You can do exactly the same thing, with the same resulting types, without any type annotations. They will be inferred by the compiler, thus saving all that extra typing:

const el = document.getElementById('content');

if (el) {
  const definitelyAnElement = el;
}
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • 1
    Thanks. I do like adding the type even if it can be inferred though! I am glad both are supported. – bnieland Feb 07 '13 at 03:48
  • In my experience it returns HTMLElement | null which is not comparable to HTMLElement in typescript. So unless processing stops here this is not the whole story – Clarence Sep 13 '18 at 18:27
  • 1
    Hi @Clarence - yes, this changed since 2013, so I'll some additional information around that. – Fenton Sep 21 '18 at 16:19
  • The declaration of the element must be: `const el = document.getElementById('content') as HTMLElement;` – Orici Oct 30 '18 at 08:02
  • @Orici - I disagree. The type returned from `getElementById` is `HTMLElement` or with strict null checks `HTMLElement | null`. In either case, allowing the `el` variable to have an inferred type is the path to success, as per the last example in the answer. – Fenton Oct 30 '18 at 09:39
17

Okay: weird syntax!

var el: HTMLElement = document.getElementById('content');

fixes the problem. I wonder why the example didn't do this in the first place?

complete code:

class Greeter {
    element: HTMLElement;
    span: HTMLElement;
    timerToken: number;

    constructor (element: HTMLElement) { 
        this.element = element;
        this.element.innerText += "The time is: ";
        this.span = document.createElement('span');
        this.element.appendChild(this.span);
        this.span.innerText = new Date().toUTCString();
    }

    start() {
        this.timerToken = setInterval(() => this.span.innerText = new Date().toUTCString(), 500);
    }

    stop() {
        clearTimeout(this.timerToken);
    }

}

window.onload = () => {
    var el: HTMLElement = document.getElementById('content');
    var greeter = new Greeter(el);
    greeter.start();
};
bnieland
  • 6,047
  • 4
  • 40
  • 66
7

In JavaScript you declare variables or functions by using the keywords var, let or function. In TypeScript classes you declare class members or methods without these keywords followed by a colon and the type or interface of that class member.

It’s just syntax sugar, there is no difference between:

var el: HTMLElement = document.getElementById('content');

and:

var el = document.getElementById('content');

On the other hand, because you specify the type you get all the information of your HTMLElement object.

Nathan Arthur
  • 8,287
  • 7
  • 55
  • 80
4

try use typing for elementHTML like this:

window.onload = () => {
    const el= <HTMLElement>document.getElementById('content');
    constgreeter = new Greeter(el);
    greeter.start();
};
Anjasmara Dwi.S
  • 303
  • 1
  • 8
  • Without null checking, this is prone to getting a type error in the event of no #content element existing in the document. – Eric N May 05 '22 at 14:22
2

Note that const declarations are block-scoped.

const el: HTMLElement | null = document.getElementById('content');

if (el) {
  const definitelyAnElement: HTMLElement = el;
}

So the value of definitelyAnElement is not accessible outside of the {}.

(I would have commented above, but I do not have enough Reputation apparently.)

Dale Hurtt
  • 61
  • 3