TypeScript: problems with type system

我只是在 VisualStudio2012中测试打字脚本,它的类型系统有一个问题。我的 html 站点有一个 id 为“ myscape”的画布标记。我试着在这块画布上画一个长方形。这是密码

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

不幸的是 VisualStudio 抱怨说

the property 'getContext' does no exist on value of type “ HTMLElement”

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

有生成错误。要继续并运行最后一个 建造成功了吗?

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

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

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

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

好吧,我完全支持静态类型,但这使得语言无法使用。类型系统要我做什么?

更新:

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

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

这个问题似乎在0.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 参见“ Overload on Constants”部分,其中显式显示了画布标记。

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');

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

If fixed it by first casting to 'any':

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

或者这种方式(它有相同的效果)

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

现在,mySvg 被强类型化为 SVGSVGElement。

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

虽然其他答案会促进类型断言(它们就是这样的ー TypeScript 没有实际改变类型的 模型铸造,它们只是抑制类型检查错误的一种方式) ,但是理智上诚实的解决问题的方法是倾听错误信息。

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

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

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

用法:

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


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

参见 运动场

这是一个老话题... 也许死到2012年,但令人兴奋和新的 VS 代码和打字机。

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


你可能必须在你的 tsconfig.json中将 DOM加到 compilerOptions.lib

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

I'd recommend

let canvas = document.getElementById('canvas') as
HTMLCanvasElement;