TypeScript:型システムの問題


101

私はVisualStudio 2012でtypescriptをテストしているだけで、その型システムに問題があります。私のHTMLサイトには、「mycanvas」というIDのcanvasタグがあります。このキャンバスに長方形を描こうとしています。これがコードです

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

残念ながらVisualStudioは不平を言います

プロパティ「getContext」は、タイプ「HTMLElement」の値に存在しません

2行目をエラーとしてマークします。これは単なる警告だと思いましたが、コードはコンパイルされません。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);

しかし、型システムはまだ満足されていませんでした。これが新しいエラーメッセージです。今回は1行目にあります。

「HTMLElement」を「HTMLCanvasElement」に変換できません:タイプ「HTMLElement」にタイプ「HTMLCanvasElement」のプロパティ「toDataURL」がありません

まあ、私はすべて静的型付けに専念していますが、これにより言語が使用できなくなります。型システムは私に何をしたいですか?

更新:

Typescriptは確かに動的呼び出しをサポートしておらず、私の問題はタイプキャストで解決できます。私の質問は基本的にはこの1つのTypeScriptの複製です:HTMLElementのキャスト

回答:


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
キャンバスコンテキストCanvasRenderingContext2Dでは、anyタイプの代わりに使用する方がよいことを言及する価値があります。
Ivan Kochurkin 2014年

3
活字体1.8以来、それは一定の文字列引数を認識し"2d"、そして.getContext("2d")型に戻りますCanvasRenderingContext2D。明示的にキャストする必要はありません。
Markus Jarderot

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実際にはですHTMLCanvasElementCanvasRenderingContext2D、ブラウザではサポートされていません。

コンパイラーにシャットダウンするように指示する代わりに(そして、不要なエラーメッセージ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

同じ問題がありましたが、HTMLCanvasElementの代わりにSVGSVGElementを使用しました。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コードとタイプスクリプトの新機能です。

これをVS Codeで次のパッケージ参照を使用して機能させるには、以下を実行する必要がありました。

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

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

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

Typescriptバージョン:

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


0

にを追加DOMする必要がある場合がありcompilerOptions.libますtsconfig.json

// 'tsconfig.json'
 {
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "lib": [
      "es5",
      "DOM",
      "esnext"
    ]
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.