How do I get typescript to stop complaining about functions it doesn't know about?
Asked Answered
B

7

25

I'm using Typescript for a web app that needs to use the JavaScript full screen API. The full screen API isn't officially supported yet, so you have to use vendor prefixes. Here's my code, based on the sample from MDN:

function toggleFullScreen(element: JQuery) {
    var fs = element[0];
    if (!document.fullscreenElement &&    // alternative standard method
        !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {  // current working methods
        if (fs.requestFullscreen) {
            fs.requestFullscreen();
        } else if (fs.msRequestFullscreen) {
            fs.msRequestFullscreen();
        } else if (fs.mozRequestFullScreen) {
            fs.mozRequestFullScreen();
        } else if (fs.webkitRequestFullscreen) {
            fs.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
        }
    } else {
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        }
    }
}

However in my IDE (Visual Studio, but this would happen anywhere), I get errors like:

The property 'fullscreenElement' does not exist on value of type 'Document'.
The property 'mozFullScreenElement' does not exist on value of type 'Document'.
The property 'webkitFullscreenElement' does not exist on value of type 'Document'.  

Of course TypeScript can't know that these functions exist, but nor do I want to re-declare document as any just to get rid of these errors, because then I'll lose all the other type hints.

What is the solution here? How do I get TypeScript to stop complaining but keep as many type annotations as I can?

Bleachers answered 23/9, 2014 at 11:24 Comment(1)
Perhaps create a .d.ts file? (Or in this specific case, find one someone else has already created -- and if not, share the one you do...)Decentralize
H
27

Simplistically, you could add those items to the Document interface and the errors would go away.

interface Document {
    exitFullscreen: any;
    mozCancelFullScreen: any;
    webkitExitFullscreen: any;
    fullscreenElement: any;
    mozFullScreenElement: any;
    webkitFullscreenElement: any;
}

You could add full type information for each of these, even the simple:

interface Document {
    exitFullscreen: () => void;
    mozCancelFullScreen: () => void;
    webkitExitFullscreen: () => void;
    fullscreenElement: () => void;
    mozFullScreenElement: () => void;
    webkitFullscreenElement: () => void;
}

This would prevent them being mis-used.

For static properties, you may just need to make the type dynamic, the important part in the example below is the type assertion on Element, i.e. (<any>Element):

fs.webkitRequestFullscreen((<any>Element).ALLOW_KEYBOARD_INPUT);
Hebner answered 23/9, 2014 at 12:4 Comment(0)
C
29

I have the same problem. Just use 'Square bracket' notation to solve it.

 if (document['exitFullscreen']) {
    document['exitFullscreen']();
 } else if (document['webkitExitFullscreen']) {
    document['webkitExitFullscreen']();
 } else if (document['mozCancelFullScreen']) {
    document['mozCancelFullScreen']();
 } else if (document['msExitFullscreen']) {
    document['msExitFullscreen']();
 }
Coloration answered 16/7, 2017 at 10:57 Comment(1)
This doesn't work. Nor does: javascript if ("webkitExitFullscreen" in document) { document.webkitExitFullscreen(); } Friedland
H
27

Simplistically, you could add those items to the Document interface and the errors would go away.

interface Document {
    exitFullscreen: any;
    mozCancelFullScreen: any;
    webkitExitFullscreen: any;
    fullscreenElement: any;
    mozFullScreenElement: any;
    webkitFullscreenElement: any;
}

You could add full type information for each of these, even the simple:

interface Document {
    exitFullscreen: () => void;
    mozCancelFullScreen: () => void;
    webkitExitFullscreen: () => void;
    fullscreenElement: () => void;
    mozFullScreenElement: () => void;
    webkitFullscreenElement: () => void;
}

This would prevent them being mis-used.

For static properties, you may just need to make the type dynamic, the important part in the example below is the type assertion on Element, i.e. (<any>Element):

fs.webkitRequestFullscreen((<any>Element).ALLOW_KEYBOARD_INPUT);
Hebner answered 23/9, 2014 at 12:4 Comment(0)
L
11

Adding to the answer of Fenton we added some details to these types and declared them in a regular TypeScript Module with imports/exports. This means you also have to declare the global namespace:

declare global {
  interface Document {
    mozCancelFullScreen?: () => Promise<void>;
    msExitFullscreen?: () => Promise<void>;
    webkitExitFullscreen?: () => Promise<void>;
    mozFullScreenElement?: Element;
    msFullscreenElement?: Element;
    webkitFullscreenElement?: Element;
  }

  interface HTMLElement {
    msRequestFullscreen?: () => Promise<void>;
    mozRequestFullscreen?: () => Promise<void>;
    webkitRequestFullscreen?: () => Promise<void>;
  }
}
Lanta answered 21/1, 2020 at 13:54 Comment(2)
I have added these codes to global.d.ts under src folder, however, it still continue giving error.Investigator
The correct name for the Mozilla function is mozRequestFullScreen with a capital S.Reddick
S
2

Steve fenton's answer is excellent and in the long run that is what you should do. Remember Types are documentation and will help the next developer.

Bad but proving that typescript is permissive if you want it to be

Purely as a thought experiment you can create a local variable to shadow the global one and explicitly type it to be any only once:

function toggleFullScreen(element: JQuery) {
    var document:any = window.document;
    document.AnythingCanHappen = 123; // No error 
}

And for more fancy ones (grab from an outer scope):

var Element_Copy=Element; 
function toggleFullScreen(element: JQuery) {
    var Element:any = Element_Copy; 
    Element.ShootMyself = true;
} 

Complete example:

var Element_Copy=Element;                         // Magic
function toggleFullScreen(element: JQuery) {
    var document:any = window.document;           // Magic
    var Element:any = Element_Copy;               // Magic
    var fs = element[0];
    if (!document.fullscreenElement &&    // alternative standard method
        !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {  // current working methods
        if (fs.requestFullscreen) {
            fs.requestFullscreen();
        } else if (fs.msRequestFullscreen) {
            fs.msRequestFullscreen();
        } else if (fs.mozRequestFullScreen) {
            fs.mozRequestFullScreen();
        } else if (fs.webkitRequestFullscreen) {
            fs.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
        }
    } else {
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.webkitExitFullscreen) {
            document.webkitExitFullscreen();
        }
    }
}
Sanford answered 23/9, 2014 at 12:48 Comment(0)
L
2

I ran into the same problem today and the solutions that I've seen here did not completely work on our angular 7 project.

Here is what we've done to make it work. We've first created an interface for Document as mentioned above. (we only needed 'fullscreenElement' in our case, but you can add any you like).

export interface Document extends HTMLDocument {
    fullscreenElement: any;
}

Imported this into our component without implementing it.

import { Document } from '../interfaces/document';

Then, instead of using document.fullscreenElement you can cast document to the expanded type we created above, like this:

(document as Document).fullscreenElement

This allowed us to make a fullscreen change detector like this:

@HostListener('document:fullscreenchange', ['$event']) onfullscreenchange(e) {
  if ((document as Document).fullscreenElement) {
    this.isFullscreen = true;
  } else {
    this.isFullscreen = false;
  }
}

Typescript will now accept these newly defined properties.

Lorient answered 9/1, 2020 at 16:37 Comment(0)
S
2

You can just cast to "any" before accessing the properties and functions.

Example:

if ((<any> canvas).mozRequestFullScreen) {
    (<any> canvas).mozRequestFullScreen();
}
Scouting answered 2/8, 2020 at 21:21 Comment(0)
B
1

This is not recommended but another solution to stop the compiler complaining:

const document: any = window.document;
Bacardi answered 9/2, 2018 at 10:58 Comment(2)
Why is it not recommended?Akel
By side stepping TypeScript you side step the benefits of type safety. There are all sorts of cases where things could slip through the compiler by making them any. The general expectation is to try to find the strict way of expressing something but you'll plenty of cases in production where developers don't have the patience or experience and just take "the easy way".Friedland

© 2022 - 2024 — McMap. All rights reserved.