TypeScript: problemas con el sistema de tipos

101

Solo estoy probando mecanografiado en VisualStudio 2012 y tengo un problema con su sistema de tipos. Mi sitio html tiene una etiqueta de lienzo con la identificación "mycanvas". Estoy tratando de dibujar un rectángulo en este lienzo. Aqui esta el codigo

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

Desafortunadamente, VisualStudio se queja de que

la propiedad 'getContext' no existe en el valor de tipo 'HTMLElement'

Marca la segunda línea como un error. Pensé que esto sería simplemente una advertencia, pero el código no se compila. VisualStudio dice que

hubo errores de compilación. ¿Le gustaría continuar y ejecutar la última compilación exitosa?

No me gustó este error en absoluto. ¿Por qué no hay una invocación de método dinámico? Después de todo, el método getContext definitivamente existe en mi elemento de lienzo. Sin embargo, pensé que este problema sería fácil de resolver. Acabo de agregar una anotación de tipo para lienzo:

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

Pero el sistema de tipos todavía no estaba satisfecho. Aquí está el nuevo mensaje de error, esta vez en la primera línea:

No se puede convertir 'HTMLElement' a 'HTMLCanvasElement': Al tipo 'HTMLElement' le falta la propiedad 'toDataURL' del tipo 'HTMLCanvasElement'

Bueno, me inclino por la escritura estática, pero esto hace que el idioma sea inutilizable. ¿Qué quiere el sistema de tipos que haga?

ACTUALIZAR:

De hecho, TypeScript no tiene soporte para la invocación dinámica y mi problema se puede resolver con typecasts. Mi pregunta es básicamente un duplicado de este TypeScript: casting HTMLElement

lhk
fuente

Respuestas:

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

o usando búsqueda dinámica con el anytipo (sin verificación de tipo):

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

Se puede ver en los diferentes tipos de lib.d.ts .

Markus Jarderot
fuente
9
Vale la pena mencionar que es mejor usar en CanvasRenderingContext2Dlugar de anyescribir para el contexto del lienzo.
Ivan Kochurkin
3
Desde TypeScript 1.8, reconocerá el argumento de cadena constante "2d"y .getContext("2d")volverá con el tipo CanvasRenderingContext2D. No es necesario emitirlo explícitamente.
Markus Jarderot
13
const canvas =  document.getElementById('stage') as HTMLCanvasElement;
Halil Kocaerkek
fuente
7
Explique por qué este código ayuda a resolver el problema de OP en lugar de solo una respuesta de código. ¿Qué hace su código de manera diferente, cómo ayuda?
Copias / pegas y funciona. ¿Qué explicar aquí? Si esto no funciona para usted, entonces usted debe explicar su problema y pedir más específicamente.
lenooh
6

Mientras que otras respuestas promueven las afirmaciones de tipo (eso es lo que son: TypeScript no tiene conversiones de tipo que realmente cambien el tipo; son simplemente una forma de suprimir los errores de verificación de tipos), la forma intelectualmente honesta de abordar su problema es escuchar el error de mensajes.

En tu caso, hay 3 cosas que pueden salir mal:

  • document.getElementById("mycanvas")podría regresar null, porque no se encuentra ningún nodo de esa identificación (podría haber sido renombrado, no inyectado en el documento todavía, alguien podría haber intentado ejecutar su función en un entorno sin acceso a DOM)
  • document.getElementById("mycanvas") podría devolver una referencia a un elemento DOM, pero este elemento DOM no es un HTMLCanvasElement
  • document.getElementById("mycanvas")devolvió un valor válido HTMLElement, de hecho es un HTMLCanvasElement, pero CanvasRenderingContext2Dno es compatible con el navegador.

En lugar de decirle al compilador que se calle (y posiblemente encontrarse en una situación en la que se arroje un mensaje de error inútil como Cannot read property 'getContext' of null), le recomiendo que tome el control sobre los límites de su aplicación.

Asegúrese de que el 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;
}

Asegúrese de que el navegador admita el contexto de representación

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

Uso:

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

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

Consulte Zona de juegos de TypeScript .

Karol Majewski
fuente
1

Tuve el mismo problema, pero con SVGSVGElement en lugar de HTMLCanvasElement. La conversión a SVGSVGElement produjo un error en tiempo de compilación:

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

No se puede convertir 'HTMLElement' a 'SVGSVGElement': Al
tipo 'HTMLElement' le falta la propiedad 'width' del tipo 'SVGSVGElement'.
Al tipo 'SVGSVGElement' le falta la propiedad 'onmouseleave' del tipo 'HTMLElement'.

Si lo solucionaste lanzando primero a 'cualquiera':

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

o de esta manera (tiene el mismo efecto)

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

Ahora mySvg está fuertemente tipado como SVGSVGElement.

Eduardo
fuente
0

Este es un tema antiguo ... quizás muerto en 2012, pero emocionante y nuevo para VS Code y mecanografiado.

Tuve que hacer lo siguiente para que esto funcionara en VS Code con las siguientes referencias de paquetes.

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

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

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

Versión mecanografiada:

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

un agente
fuente
0

Puede que tenga que añadir DOMa compilerOptions.liben su tsconfig.json.

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