TypeScript: problems with type system

血红的双手。 提交于 2019-12-02 16:02:59
Markus Jarderot
var canvas = <HTMLCanvasElement> document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");

or using dynamic lookup with the any type (no typechecking):

var canvas : any = document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");

You can look at the different types in lib.d.ts.

const canvas =  document.getElementById('stage') as HTMLCanvasElement;

It seems this is being corrected in the .9 version of TypeScript: http://blogs.msdn.com/b/typescript/archive/2013/03/25/working-on-typescript-0-9-generics-overload-on-constants-and-compiler-performance.aspx See the section on "Overload on Constants" where the canvas tag is explicitly shown.

I had the same problem, but with SVGSVGElement instead of HTMLCanvasElement. Casting to SVGSVGElement gave a compile-time error:

var mySvg = <SVGSVGElement>document.getElementById('mySvg');

Cannot convert 'HTMLElement' to 'SVGSVGElement':
Type 'HTMLElement' is missing property 'width' from type 'SVGSVGElement'.
Type 'SVGSVGElement' is missing property 'onmouseleave' from type 'HTMLElement'.

If fixed it by first casting to 'any':

var mySvg = <SVGSVGElement><any>document.getElementById('mySvg');

or this way (it has the identical effect)

var mySvg: SVGSVGElement = <any>document.getElementById('mySvg');

Now mySvg is strongly typed as SVGSVGElement.

While other answers promote type assertions (that's what they are — TypeScript doesn't have type casts that actually change the type; they are merely a way of suppressing type checking errors), the intellectually honest way to approach your problem is to listen to the error messages.

In your case, there are 3 things that can go wrong:

  • document.getElementById("mycanvas") might return null, because no node of that id is found (it might have been renamed, not injected to the document yet, someone might have tried running your function in an environment without access to DOM)
  • document.getElementById("mycanvas") might return a reference to a DOM element, but this DOM element is not a HTMLCanvasElement
  • document.getElementById("mycanvas") did return a valid HTMLElement, it is indeed an HTMLCanvasElement, but the CanvasRenderingContext2D is not supported by the browser.

Instead of telling the compiler to shut up (and possibly finding yourself in a situation where a useless error message like Cannot read property 'getContext' of null is thrown), I recommend taking control over your application boundaries.

Make sure the element contains a HTMLCanvasElement

const getCanvasElementById = (id: string): HTMLCanvasElement => {
    const canvas = document.getElementById(id);

    if (!(canvas instanceof HTMLCanvasElement)) {
        throw new Error(`The element of id "${id}" is not a HTMLCanvasElement. Make sure a <canvas id="${id}""> element is present in the document.`);
    }

    return canvas;
}

Make sure the rendering context is supported by the browser

const getCanvasRenderingContext2D = (canvas: HTMLCanvasElement): CanvasRenderingContext2D => {
    const context = canvas.getContext('2d');

    if (context === null) {
        throw new Error('This browser does not support 2-dimensional canvas rendering contexts.');
    }

    return context;
}

Usage:

const ctx: CanvasRenderingContext2D = getCanvasRenderingContext2D(getCanvasElementById('mycanvas'))

ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 100, 100);

See TypeScript Playground.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!