TypeScript: problemi con il sistema di caratteri


101

Sto solo testando il dattiloscritto in VisualStudio 2012 e ho un problema con il suo sistema di tipi. Il mio sito html ha un tag canvas con l'id "mycanvas". Sto cercando di disegnare un rettangolo su questa tela. Ecco il codice

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

Purtroppo VisualStudio se ne lamenta

la proprietà 'getContext' non esiste nel valore di tipo 'HTMLElement'

Contrassegna la seconda riga come errore. Ho pensato che questo fosse solo un avvertimento ma il codice non viene compilato. VisualStudio lo dice

c'erano errori di costruzione. Vuoi continuare ed eseguire l'ultima build riuscita?

Non mi è piaciuto affatto questo errore. Perché non c'è invocazione di metodi dinamici? Dopo tutto il metodo getContext esiste sicuramente sul mio elemento canvas. Tuttavia ho pensato che questo problema sarebbe stato facile da risolvere. Ho appena aggiunto un'annotazione di tipo per la tela:

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

Ma il sistema dei tipi non era ancora soddisfatto. Ecco il nuovo messaggio di errore, questa volta nella prima riga:

Impossibile convertire "HTMLElement" in "HTMLCanvasElement": nel tipo "HTMLElement" manca la proprietà "toDataURL" dal tipo "HTMLCanvasElement"

Bene, sono tutto pronto per la digitazione statica, ma questo rende il linguaggio inutilizzabile. Cosa vuole che faccia il sistema di tipi?

AGGIORNARE:

Typescript non ha infatti alcun supporto per l'invocazione dinamica e il mio problema può essere risolto con typecast. La mia domanda è fondamentalmente un duplicato di questo TypeScript: casting HTMLElement

Risposte:


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

o utilizzando la ricerca dinamica con il anytipo (senza controllo dei tipi):

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

Potete guardare i vari tipi delle lib.d.ts .


9
Vale la pena ricordare che è meglio usare CanvasRenderingContext2Dinvece del anytipo per il contesto della tela.
Ivan Kochurkin

3
A partire da TypeScript 1.8, riconoscerà l'argomento stringa costante "2d"e .getContext("2d")restituirà il tipo CanvasRenderingContext2D. Non è necessario trasmetterlo esplicitamente.
Markus Jarderot

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

7
Spiega perché questo codice aiuta a risolvere il problema di OP piuttosto che una semplice risposta in codice. Cosa fa il tuo codice in modo diverso, come aiuta?

Copia / incolla e funziona. Cosa c'è da spiegare qui? Se non funziona per voi, allora si dovrebbe spiegare il problema e chiedere più specificamente.
Lenooh

6

Mentre altre risposte promuovono asserzioni di tipo (ecco cosa sono: TypeScript non ha cast di tipo che effettivamente cambiano il tipo; sono semplicemente un modo per sopprimere gli errori di controllo del tipo), il modo intellettualmente onesto per affrontare il tuo problema è ascoltare il messaggio di errore.

Nel tuo caso, ci sono 3 cose che possono andare storte:

  • document.getElementById("mycanvas")potrebbe tornare null, perché non viene trovato alcun nodo di quell'id (potrebbe essere stato rinominato, non ancora inserito nel documento, qualcuno potrebbe aver provato a eseguire la tua funzione in un ambiente senza accesso a DOM)
  • document.getElementById("mycanvas") potrebbe restituire un riferimento a un elemento DOM, ma questo elemento DOM non è un HTMLCanvasElement
  • document.getElementById("mycanvas")ha restituito un valido HTMLElement, è effettivamente un HTMLCanvasElement, ma CanvasRenderingContext2Dnon è supportato dal browser.

Invece di dire al compilatore di stare zitto (e possibilmente trovarti in una situazione in cui Cannot read property 'getContext' of nullviene lanciato un messaggio di errore inutile come ), ti consiglio di prendere il controllo sui confini dell'applicazione.

Assicurati che l'elemento contenga un 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;
}

Assicurati che il contesto di rendering sia supportato dal 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;
}

Utilizzo:

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

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

Vedere TypeScript Playground .



1

Ho avuto lo stesso problema, ma con SVGSVGElement invece di HTMLCanvasElement. La trasmissione a SVGSVGElement ha dato un errore in fase di compilazione:

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

Impossibile convertire "HTMLElement" in "SVGSVGElement": nel
tipo "HTMLElement" manca la proprietà "width" dal tipo "SVGSVGElement".
Di tipo "SVGSVGElement" manca la proprietà "onmouseleave" dal tipo "HTMLElement".

Se risolto il problema eseguendo prima il casting su "any":

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

o in questo modo (ha lo stesso effetto)

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

Ora mySvg è fortemente tipizzato come SVGSVGElement.


0

Questo è un vecchio argomento ... forse morto al 2012, ma eccitante e nuovo per VS Code e dattiloscritto.

Ho dovuto fare quanto segue per farlo funzionare in VS Code con i seguenti riferimenti ai pacchetti.

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

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

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

Versione dattiloscritto:

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

0

Potrebbe essere necessario aggiungere DOMa compilerOptions.libnel tuo file tsconfig.json.

// 'tsconfig.json'
 {
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "lib": [
      "es5",
      "DOM",
      "esnext"
    ]
  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.