TypeScript: проблемы с системой типов


102

Я просто тестирую машинописный текст в 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 определенно существует в моем элементе холста. Однако я думал, что эту проблему будет легко решить. Я только что добавил аннотацию типа для холста:

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 действительно нет поддержки динамического вызова, и мою проблему можно решить с помощью преобразования типов. Мой вопрос в основном дублирует этот TypeScript: кастинг HTMLElement

Ответы:


225
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вместо anytype.
Иван Кочуркин

3
Начиная с TypeScript 1.8, он распознает постоянный строковый аргумент "2d"и .getContext("2d")вернет его с типом CanvasRenderingContext2D. Вам не нужно приводить его явно.
Markus Jarderot

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

7
Пожалуйста, объясните, почему этот код помогает решить проблему OP, а не просто кодовый ответ. Что ваш код по-другому делает, как он помогает?

Вы копируете / вставляете, и это работает. Что тут объяснять? Если это не работает для вас, то вы должны объяснить свою проблему и попросить более конкретно.
lenooh

7

В то время как другие ответы продвигают утверждения типа (вот что они есть - 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 Playground .



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 Code и машинописного текста.

Мне пришлось сделать следующее, чтобы заставить это работать в VS Code со следующими ссылками на пакеты.

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.