TypeScript: masalah dengan sistem tipe

101

Saya baru saja menguji skrip ketikan di VisualStudio 2012 dan memiliki masalah dengan sistem tipenya. Situs html saya memiliki tag kanvas dengan id "mycanvas". Saya mencoba menggambar persegi panjang di kanvas ini. Ini kodenya

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

Sayangnya VisualStudio mengeluhkan hal itu

properti 'getContext' tidak ada pada nilai tipe 'HTMLElement'

Ini menandai baris kedua sebagai kesalahan. Saya pikir ini hanya akan menjadi peringatan tetapi kode tidak dapat dikompilasi. VisualStudio mengatakan itu

ada kesalahan pembuatan. Apakah Anda ingin melanjutkan dan menjalankan build terakhir yang berhasil?

Saya sama sekali tidak menyukai kesalahan ini. Mengapa tidak ada pemanggilan metode dinamis? Bagaimanapun, metode getContext pasti ada di elemen kanvas saya. Namun saya pikir masalah ini akan mudah diselesaikan. Saya baru saja menambahkan anotasi tipe untuk kanvas:

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

Tapi sistem tipe masih belum memuaskan. Inilah pesan kesalahan baru, kali ini di baris pertama:

Tidak dapat mengonversi 'HTMLElement' menjadi 'HTMLCanvasElement': Jenis 'HTMLElement' tidak memiliki properti 'toDataURL' dari jenis 'HTMLCanvasElement'

Yah, saya habis untuk mengetik statis tetapi ini membuat bahasanya tidak dapat digunakan. Apa yang sistem tipe ingin saya lakukan?

MEMPERBARUI:

Ketik memang tidak mendukung pemanggilan dinamis dan masalah saya dapat diselesaikan dengan typecasts. Pertanyaan saya pada dasarnya adalah duplikat dari TypeScript yang satu ini : casting HTMLElement

lhk
sumber

Jawaban:

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

atau menggunakan pencarian dinamis dengan anytipe (tanpa pemeriksaan ketik):

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

Anda dapat melihat berbagai jenis di lib.d.ts .

Markus Jarderot
sumber
9
Perlu disebutkan bahwa lebih baik digunakan CanvasRenderingContext2Ddaripada anyjenis untuk konteks kanvas.
Ivan Kochurkin
3
Sejak TypeScript 1.8, itu akan mengenali argumen string konstan "2d", dan .getContext("2d")akan kembali dengan tipe CanvasRenderingContext2D. Anda tidak perlu mentransmisikannya secara eksplisit.
Markus Jarderot
13
const canvas =  document.getElementById('stage') as HTMLCanvasElement;
Halil Kocaerkek
sumber
7
Tolong jelaskan mengapa kode ini membantu menyelesaikan masalah OP daripada hanya jawaban kode. Apa fungsi kode Anda secara berbeda, apa manfaatnya?
Anda menyalin / menempel dan berhasil. Apa yang harus dijelaskan di sini? Jika tidak berhasil, Anda harus menjelaskan masalah Anda dan bertanya lebih spesifik.
lenooh
6

Sementara jawaban lain mempromosikan pernyataan tipe (itulah mereka - TypeScript tidak memiliki tipe cast yang benar-benar mengubah tipe; mereka hanyalah cara untuk menekan kesalahan pengecekan tipe), cara yang jujur ​​secara intelektual untuk mendekati masalah Anda adalah dengan mendengarkan pesan kesalahan.

Dalam kasus Anda, ada 3 hal yang bisa salah:

  • document.getElementById("mycanvas")mungkin kembali null, karena tidak ada node dari id tersebut yang ditemukan (mungkin telah diganti namanya, belum dimasukkan ke dokumen, seseorang mungkin telah mencoba menjalankan fungsi Anda di lingkungan tanpa akses ke DOM)
  • document.getElementById("mycanvas") mungkin mengembalikan referensi ke elemen DOM, tetapi elemen DOM ini bukan HTMLCanvasElement
  • document.getElementById("mycanvas")memang mengembalikan yang valid HTMLElement, itu memang sebuah HTMLCanvasElement, tetapi CanvasRenderingContext2Dtidak didukung oleh browser.

Alih-alih memberi tahu kompiler untuk tutup mulut (dan mungkin menemukan diri Anda dalam situasi di mana pesan kesalahan yang tidak berguna seperti Cannot read property 'getContext' of nulldilemparkan), saya sarankan untuk mengambil kendali atas batas aplikasi Anda.

Pastikan elemen tersebut berisi 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;
}

Pastikan konteks rendering didukung oleh 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;
}

Pemakaian:

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

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

Lihat TypeScript Playground .

Karol Majewski
sumber
1

Saya memiliki masalah yang sama, tetapi dengan SVGSVGElement, bukan HTMLCanvasElement. Mentransmisikan ke SVGSVGElement memberikan kesalahan waktu kompilasi:

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

Tidak dapat mengubah 'HTMLElement' menjadi 'SVGSVGElement':
Ketik 'HTMLElement' tidak memiliki properti 'width' dari jenis 'SVGSVGElement'.
Jenis 'SVGSVGElement' tidak memiliki properti 'onmouseleave' dari jenis 'HTMLElement'.

Jika memperbaikinya dengan mentransmisikan terlebih dahulu ke 'any':

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

atau dengan cara ini (efeknya sama)

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

Sekarang mySvg sangat diketik sebagai SVGSVGElement.

Edward
sumber
0

Ini adalah topik lama ... mungkin sudah mati sampai tahun 2012, tetapi menarik dan baru untuk VS Code dan skrip ketikan.

Saya harus melakukan hal berikut untuk mendapatkan ini berfungsi di VS Code dengan referensi paket berikut.

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

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

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

Versi Script:

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

Anda mungkin harus menambahkan DOMke compilerOptions.libdalam tsconfig.json.

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