TypeScript: Probleme mit dem Typsystem

101

Ich teste nur Typoskript in VisualStudio 2012 und habe ein Problem mit dem Typsystem. Meine HTML-Site hat ein Canvas-Tag mit der ID "mycanvas". Ich versuche ein Rechteck auf diese Leinwand zu zeichnen. Hier ist der Code

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

Leider beschwert sich VisualStudio darüber

Die Eigenschaft 'getContext' ist für den Wert vom Typ 'HTMLElement' nicht vorhanden.

Es markiert die zweite Zeile als Fehler. Ich dachte, dies wäre nur eine Warnung, aber der Code wird nicht kompiliert. VisualStudio sagt das

Es gab Build-Fehler. Möchten Sie fortfahren und den letzten erfolgreichen Build ausführen?

Dieser Fehler hat mir überhaupt nicht gefallen. Warum gibt es keinen dynamischen Methodenaufruf? Immerhin existiert die Methode getContext definitiv auf meinem Canvas-Element. Ich dachte jedoch, dieses Problem wäre leicht zu lösen. Ich habe gerade eine Typanmerkung für Canvas hinzugefügt:

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

Aber das Typensystem war immer noch nicht zufrieden. Hier ist die neue Fehlermeldung, diesmal in der ersten Zeile:

'HTMLElement' kann nicht in 'HTMLCanvasElement' konvertiert werden: Typ 'HTMLElement' fehlt die Eigenschaft 'toDataURL' vom Typ 'HTMLCanvasElement'

Nun, ich bin ganz auf statisches Tippen aus, aber das macht die Sprache unbrauchbar. Was möchte das Typsystem von mir?

AKTUALISIEREN:

Typescript unterstützt in der Tat keinen dynamischen Aufruf und mein Problem kann mit Typecasts gelöst werden. Meine Frage ist im Grunde ein Duplikat dieses einen TypeScript: Casting HTMLElement

lhk
quelle

Antworten:

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

oder dynamische Suche mit dem anyTyp verwenden (keine Typüberprüfung):

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

Sie können sich die verschiedenen Typen in lib.d.ts ansehen .

Markus Jarderot
quelle
9
Es ist erwähnenswert, dass es besser ist, CanvasRenderingContext2Danstelle von anyTyp für den Canvas-Kontext zu verwenden.
Ivan Kochurkin
3
Seit TypeScript 1.8 erkennt es das konstante Zeichenfolgenargument "2d"und .getContext("2d")gibt es mit dem Typ zurück CanvasRenderingContext2D. Sie müssen es nicht explizit umsetzen.
Markus Jarderot
13
const canvas =  document.getElementById('stage') as HTMLCanvasElement;
Halil Kocaerkek
quelle
7
Bitte erläutern Sie, warum dieser Code zur Lösung des OP-Problems beiträgt und nicht nur eine Code-Antwort. Was macht Ihr Code anders, wie hilft es?
Sie kopieren / einfügen und es funktioniert. Was ist hier zu erklären? Wenn es bei Ihnen nicht funktioniert, sollten Sie Ihr Problem erklären und genauer fragen.
Lenooh
6

Während andere Antworten Typensicherungen fördern (das ist es, was sie sind - TypeScript hat keine Typumwandlungen , die den Typ tatsächlich ändern; sie sind lediglich eine Möglichkeit, Fehler bei der Typprüfung zu unterdrücken), besteht die intellektuell ehrliche Art, sich Ihrem Problem zu nähern, darin, sich das anzuhören Fehlermeldungen.

In Ihrem Fall können drei Dinge schief gehen:

  • document.getElementById("mycanvas")Möglicherweise wird zurückgegeben null, weil kein Knoten dieser ID gefunden wurde (er wurde möglicherweise umbenannt und noch nicht in das Dokument eingefügt. Möglicherweise hat jemand versucht, Ihre Funktion in einer Umgebung ohne Zugriff auf DOM auszuführen.)
  • document.getElementById("mycanvas") Möglicherweise wird ein Verweis auf ein DOM-Element zurückgegeben, aber dieses DOM-Element ist kein HTMLCanvasElement
  • document.getElementById("mycanvas")hat eine gültige zurückgegeben HTMLElement, es ist zwar eine HTMLCanvasElement, aber die CanvasRenderingContext2Dwird vom Browser nicht unterstützt.

Anstatt dem Compiler zu sagen, er solle den Mund halten (und sich möglicherweise in einer Situation befinden, in der eine nutzlose Fehlermeldung wie Cannot read property 'getContext' of nullangezeigt wird), empfehle ich, die Kontrolle über Ihre Anwendungsgrenzen zu übernehmen.

Stellen Sie sicher, dass das Element ein HTMLCanvasElement enthält

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

Stellen Sie sicher, dass der Renderkontext vom Browser unterstützt wird

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

Verwendung:

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

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

Siehe TypeScript-Spielplatz .

Karol Majewski
quelle
1

Ich hatte das gleiche Problem, aber mit SVGSVGElement anstelle von HTMLCanvasElement. Das Casting in SVGSVGElement ergab einen Fehler bei der Kompilierung:

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

'HTMLElement' kann nicht in 'SVGSVGElement' konvertiert werden:
Typ 'HTMLElement' fehlt die Eigenschaft 'width' vom Typ 'SVGSVGElement'.
Typ 'SVGSVGElement' fehlt die Eigenschaft 'onmouseleave' vom Typ 'HTMLElement'.

Wenn es durch erstes Casting auf "any" behoben wurde:

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

oder auf diese Weise (es hat den gleichen Effekt)

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

Jetzt ist mySvg stark als SVGSVGElement typisiert.

Edward
quelle
0

Dies ist ein altes Thema ... vielleicht bis 2012 tot, aber aufregend und neu für VS Code und Typoskript.

Ich musste Folgendes tun, damit dies in VS Code mit den folgenden Paketreferenzen funktioniert.

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

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

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

Typoskript-Version:

{
    "@typescript-eslint/eslint-plugin": "^2.29.0",
    "@typescript-eslint/parser": "^2.29.0",
    "typescript": "^3.7.5"
}
ein Agent
quelle
0

Sie können hinzufügen müssen , DOMum compilerOptions.libin Ihrem tsconfig.json.

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