问题
I'm just testing typescript in VisualStudio 2012 and have a problem with its type system. My html site has a canvas tag with the id "mycanvas". I'm trying to draw a rectangle on this canvas. Here's the code
var canvas = document.getElementById("mycanvas");
var ctx: CanvasRenderingContext2D = canvas.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 100, 100);
Unfortunately VisualStudio complains that
the property 'getContext' does no exist on value of type 'HTMLElement'
It marks the second line as an error. I thought this would be merely a warning but the code does not compile. VisualStudio says that
there were build errors. Would you like to continue and run the last successful build ?
I didn't like this error at all. Why is there no dynamic method invocation ? After all the method getContext definitely exists on my canvas element. However I thought this problem would be easy to solve. I just added a type annotiation for canvas:
var canvas : HTMLCanvasElement = document.getElementById("mycanvas");
var ctx: CanvasRenderingContext2D = canvas.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 100, 100);
But the type system still wasn't satisfied. Here's the new error message, this time in the first line:
Cannot convert 'HTMLElement' to 'HTMLCanvasElement': Type 'HTMLElement' is missing property 'toDataURL' from type 'HTMLCanvasElement'
Well, I'm all out for static typing but this makes the language unusable. What does the type system want me to do ?
UPDATE:
Typescript has indeed no support for dynamic invocation and my problem can be solved with typecasts. My question is basically a duplicate of this one TypeScript: casting HTMLElement
回答1:
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.
回答2:
const canvas = document.getElementById('stage') as HTMLCanvasElement;
回答3:
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.
回答4:
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.
回答5:
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 returnnull
, 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 aHTMLCanvasElement
document.getElementById("mycanvas")
did return a validHTMLElement
, it is indeed anHTMLCanvasElement
, but theCanvasRenderingContext2D
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.
来源:https://stackoverflow.com/questions/13669404/typescript-problems-with-type-system