TypeScript:类型系统问题


101

我只是在VisualStudio 2012中测试打字稿,并且打字稿系统有问题。我的html站点有一个id为“ mycanvas”的canvas标记。我正在尝试在此画布上绘制一个矩形。这是代码

var canvas = document.getElementById("mycanvas");
var ctx: CanvasRenderingContext2D = canvas.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 100, 100);

不幸的是,VisualStudio抱怨

属性“ getContext”在类型“ HTMLElement”的值上不存在

它将第二行标记为错误。我以为这只是一个警告,但是代码无法编译。VisualStudio说

出现构建错误。您要继续并运行上一次成功的构建吗?

我根本不喜欢这个错误。为什么没有动态方法调用?毕竟,getContext方法肯定存在于我的canvas元素上。但是我认为这个问题很容易解决。我刚刚为画布添加了类型注释:

var canvas : HTMLCanvasElement = document.getElementById("mycanvas");
var ctx: CanvasRenderingContext2D = canvas.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 100, 100);

但是类型系统仍然不满意。这是新的错误消息,这次是在第一行:

无法将“ HTMLElement”转换为“ HTMLCanvasElement”:类型“ HTMLElement”缺少类型“ HTMLCanvasElement”的属性“ toDataURL”

好吧,我全力以赴进行静态类型输入,但这使该语言无法使用。类型系统要我做什么?

更新:

Typescript确实不支持动态调用,而我的问题可以通过typecast解决。我的问题基本上是这个TypeScript的重复:铸造HTMLElement

Answers:


223
var canvas = <HTMLCanvasElement> document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");

或对any类型使用动态查找(不进行类型检查):

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

您可以在lib.d.ts中查看不同的类型。


9
值得一提的是,对于画布上下文,最好使用type CanvasRenderingContext2D代替any
伊万·科彻金

3
从TypeScript 1.8开始,它将识别常量字符串参数"2d",并.getContext("2d")返回type CanvasRenderingContext2D。您不需要显式转换它。
马库斯·贾德洛

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

7
请解释为什么此代码有助于解决OP的问题,而不仅仅是代码答案。您的代码有何不同之处,有什么帮助?

您复制/粘贴即可。在这里要解释什么?如果它不为你工作,那么应该解释一下你的问题,并要求更具体。
lenooh

6

虽然其他答案会促进类型断言(就是它们的本质-TypeScript并没有真正改变类型的类型强制转换;它们只是抑制类型检查错误的一种方式),但是处理问题的明智方法是听取错误消息。

就您而言,有3件事可能会出错:

  • document.getElementById("mycanvas") 可能会回来 null,因为找不到该ID的节点(它可能已被重命名,尚未注入文档,有人可能试图在无法访问DOM的环境中运行您的函数)
  • document.getElementById("mycanvas") 可能会返回对DOM元素的引用,但是此DOM元素不是 HTMLCanvasElement
  • document.getElementById("mycanvas")确实返回了有效HTMLElement,的确是HTMLCanvasElement,但CanvasRenderingContext2D浏览器不支持。

Cannot read property 'getContext' of null我建议不要控制编译器关闭(并且可能会在抛出无用的错误消息的情况下发现自己),而是建议控制应用程序的边界。

确保元素包含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;
}

确保浏览器支持渲染上下文

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;
}

用法:

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

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

请参见TypeScript Playground



1

我遇到了同样的问题,但是使用了SVGSVGElement而不是HTMLCanvasElement。强制转换为SVGSVGElement会产生编译时错误:

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

无法将“ HTMLElement”转换为“ SVGSVGElement”:
类型“ HTMLElement”缺少类型“ SVGSVGElement”的属性“ width”。
类型“ SVGSVGElement”缺少类型“ HTMLElement”的属性“ onmouseleave”。

如果通过先将其强制转换为“ any”来解决:

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

或这种方式(效果相同)

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

现在,mySvg的类型很强,为SVGSVGElement。


0

这是一个古老的话题……也许要到2012年才能结束,但是对于VS Code和Typescript来说是令人兴奋的新事物。

我必须执行以下操作才能使其通过以下程序包引用在VS Code中起作用。

const demoCanvas: HTMLCanvasElement = document.getElementById('rfrnCanvas') as any;

        if(demoCanvas.getContext) {
            const context = demoCanvas.getContext('2d');

            if(context) {
                reactangle(context);
            }
        }

打字稿版本:

{
    "@typescript-eslint/eslint-plugin": "^2.29.0",
    "@typescript-eslint/parser": "^2.29.0",
    "typescript": "^3.7.5"
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.