TypeScript:类型系统的问题

时间:2012-12-02 12:46:37

标签: html5 types web typescript static-typing

我只是在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'在type的值上不存在   '的HTMLElement'

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

  

存在构建错误。你想继续并运行最后一个   成功建造?

我根本不喜欢这个错误。为什么没有动态方法调用?毕竟方法getContext肯定存在于我的canvas元素上。但是我认为这个问题很容易解决。我刚刚为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'缺少类型属性'toDataURL'   'HTMLCanvasElement'

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

更新:

Typescript确实不支持动态调用,我的问题可以通过类型转换来解决。我的问题基本上是这个问题的重复TypeScript: casting HTMLElement

7 个答案:

答案 0 :(得分:180)

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中查看不同的类型。

答案 1 :(得分:2)

似乎在.9版的TypeScript中正在纠正这个问题: http://blogs.msdn.com/b/typescript/archive/2013/03/25/working-on-typescript-0-9-generics-overload-on-constants-and-compiler-performance.aspx 请参阅“常量过载”部分,其中明确显示了canvas标记。

答案 2 :(得分: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。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

虽然其他答案促进了类型断言(这就是它们— 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

答案 5 :(得分:0)

这是一个古老的话题...也许在2012年就死定了,但对VS Code和打字稿来说是令人兴奋的新事物。

我必须执行以下操作才能使其在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"
}

答案 6 :(得分:0)

您可能必须将DOM中的compilerOptions.lib添加到tsconfig.json中。

// 'tsconfig.json'
 {
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "lib": [
      "es5",
      "DOM",
      "esnext"
    ]
  }
}