6

I'm using Deno to compile some TypeScript and then serve it as part of a web page, so that it is run on the browser side. I'm trying to use a canvas element on the client side, and for that I need types like CanvasRenderingContext2D or CanvasGradient, which are defined in lib.dom.d.ts, but they are not available: Deno compilation gives errors like TS2304 [ERROR]: Cannot find name 'CanvasRenderingContext2D'.. (On the other hand, type Path2D (defined in the same file) does not cause problems.)

Note: I know the types will exist in runtime when the code runs in the browser, but I want Deno to know about them in compile time.

I've tried including the .d.ts file somehow. Things I tried:

  • specifying "libs": ["deno.window", "esnext"] etc. in the compiler options (in deno.json).
  • importing the type like this:
/// <reference types="https://raw.githubusercontent.com/microsoft/TypeScript/main/lib/lib.dom.d.ts" />
  • or this:
// @deno-types="https://raw.githubusercontent.com/microsoft/TypeScript/main/lib/lib.dom.d.ts"

Some of these attempts didn't work at all, and some weren't even parsed apparently. Looks like I don't understand how Deno loads the type definitions, e.g. where does it load the Path2D type declarations from. How to fix this?

2 Answers 2

8

You need to configure Deno to use only DOM and ES types when type-checking your program. You can do this using the supported TypeScript compiler options in a Deno config file:

./deno.json:

{
  "compilerOptions": {
    "lib": [
      "esnext",
      "dom",
      "dom.iterable"
    ]
  }
}

This instructs the compiler that the program won't be running in Deno, but in a browser-like environment with those ambient global types.

Here's an example source file:

./main.ts

import {assertExists} from 'https://deno.land/[email protected]/testing/asserts.ts';

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
assertExists(ctx);

When you import from other TypeScript modules into a module like this that's

  • going to be compiled by Deno, and
  • is destined to be executed in a browser-like environment

then you'll have to bundle the result, because browsers can't process TypeScript source modules as imports:

deno bundle --config deno.json main.ts main.js

The resulting JavaScript looks like this:

// deno-fmt-ignore-file
// deno-lint-ignore-file
// This code was bundled using `deno bundle` and it's not recommended to edit it manually

const { Deno  } = globalThis;
typeof Deno?.noColor === "boolean" ? Deno.noColor : true;
new RegExp([
    "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
    "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", 
].join("|"), "g");
var DiffType;
(function(DiffType1) {
    DiffType1["removed"] = "removed";
    DiffType1["common"] = "common";
    DiffType1["added"] = "added";
})(DiffType || (DiffType = {}));
class AssertionError extends Error {
    name = "AssertionError";
    constructor(message){
        super(message);
    }
}
function assertExists(actual, msg) {
    if (actual === undefined || actual === null) {
        if (!msg) {
            msg = `actual: "${actual}" expected to not be null or undefined`;
        }
        throw new AssertionError(msg);
    }
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
assertExists(ctx);

It's safe to use the module at https://deno.land/[email protected]/testing/asserts.ts in the compilation of your program because it does runtime feature-detection of the Deno namespace before trying to use any of its APIs. Any modules which don't do that will result in a runtime error.

Sign up to request clarification or add additional context in comments.

4 Comments

There's an open issue related to the lack of tree-shaking performed by Deno.emit (which is currently the same algorithm used by deno bundle in the CLI).
Yes, I figured out the part with bundling. I'm actually using Deno.emit unstable API to run the bundler. Looks like passing lib: ["esnext", "dom"] there helps. IDE still shows the types as missing but the Deno.emit compilation succeeds. Thanks a lot.
@TPReal FWIW: it's been a long time since I've used this, but you might find some of it useful: github.com/jsejcksn/deno-userscript-bundler/tree/v0.2.3/bundler
This is explained in the docs here: deno.land/manual/typescript/…
1

You can use this if you want to include the dom types within a file.

/// <reference lib="dom" />

You can use this if you want to define it for the project deno.json

{
  "compilerOptions": {
    "lib": [
      "dom"
    ]
  }
}

1 Comment

Was able to simply add the reference without adding dom to lib. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.