TypeScript: проблеми з типовою системою


101

Я просто тестую машинопис у VisualStudio 2012 і маю проблеми з його типовою системою. На моєму html-сайті розміщено тег полотна з ідентифікатором "mycanvas". Я намагаюся намалювати прямокутник на цьому полотні. Ось код

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

На жаль, VisualStudio скаржиться на це

властивість 'getContext' не існує для значення типу 'HTMLElement'

Другий рядок він позначає як помилку. Я думав, що це буде лише попередженням, але код не складається. Про це говорить VisualStudio

були помилки побудови. Хочете продовжити та запустити останню вдалу збірку?

Мені ця помилка зовсім не сподобалась. Чому не існує динамічного виклику методу? Зрештою, метод getContext безумовно існує в моєму елементі canvas. Однак я думав, що цю проблему можна легко вирішити. Я щойно додав пояснення про тип полотна:

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

Але типова система все ще не була задоволена. Ось нове повідомлення про помилку, цього разу в першому рядку:

Неможливо перетворити 'HTMLElement' у 'HTMLCanvasElement': В 'HTMLElement' відсутнє властивість 'toDataURL' з типу 'HTMLCanvasElement'

Ну, я все готовий до статичного набору тексту, але це робить мову непридатною. Що хоче типова система?

ОНОВЛЕННЯ:

Typescript насправді не підтримує динамічне виклик, і мою проблему можна вирішити за допомогою typecasts. Моє запитання - це в основному дублікат цього типу 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типу.
Іван Кочуркін

3
Оскільки TypeScript 1.8, він розпізнає аргумент постійного рядка "2d"і .getContext("2d")повернеться з типом CanvasRenderingContext2D. Не потрібно чітко це робити.
Маркус Джардеро

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

7
Поясніть, будь ласка, чому цей код допомагає вирішити проблему ОП, а не просто відповідь на код. Що ваш код робить по-іншому, як це допомагає?

Ви копіюєте / вставляєте, і це працює. Що тут пояснити? Якщо це не працює для вас, тоді вам слід пояснити свою проблему і запитати конкретніше.
lenooh

6

Хоча інші відповіді пропагують твердження про тип (ось що вони є - у TypeScript немає типів, які фактично змінюють тип; вони є лише способом придушення помилок перевірки типу), інтелектуально чесним способом підходу до вашої проблеми є прослуховування повідомлення про помилки

У вашому випадку є 3 речі, які можуть піти не так:

  • document.getElementById("mycanvas")може повернутися null, тому що жодного вузла цього ідентифікатора не знайдено (він може бути перейменований, ще не введений в документ, хтось, можливо, намагався запустити вашу функцію в оточенні без доступу до DOM)
  • document.getElementById("mycanvas") може повернути посилання на елемент DOM, але цей елемент DOM не є HTMLCanvasElement
  • document.getElementById("mycanvas")повернув дійсну HTMLElement, вона справді є HTMLCanvasElement, але CanvasRenderingContext2Dбраузер не підтримує.

Замість того, щоб казати компілятору замовкнути (і, можливо, опинитися в ситуації, коли 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 .



1

У мене була така ж проблема, але зі SVGSVGElement замість HTMLCanvasElement. Кастинг у SVGSVGElement дав помилку часу компіляції:

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

Неможливо перетворити 'HTMLElement' у 'SVGSVGElement': У
типу 'HTMLElement' відсутнє властивість 'width' від типу 'SVGSVGElement'.
У типі 'SVGSVGElement' відсутнє властивість 'onmouseleave' від типу 'HTMLElement'.

Якщо виправлено це, попередньо відправивши його до будь-якого:

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

або таким чином (він має однаковий ефект)

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

Тепер mySvg сильно набраний як SVGSVGElement.


0

Це стара тема ... можливо, загинула до 2012 року, але захоплююча і нова для Кодексу та машинопису VS.

Мені довелося зробити наступне, щоб це працювало у коді VS із наступними посиланнями на пакет.

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

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

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

Версія машинопису:

{
    "@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"
    ]
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.