Types and Type Declarations
One of the design principles of Deno is no magical resolution. When TypeScript
is type checking a file, it only cares about the types for the file, and the
tsc compiler has a lot of logic to try to resolve those types. By default, it
expects ambiguous module specifiers with an extension, and will attempt to
look for the file under the
.ts specifier, then
.d.ts, and finally
(plus a whole other set of logic when the module resolution is set to
Deno deals with explicit specifiers.
This can cause a couple problems though. For example, let's say I want to
with a type definition file. So I have
mod.d.ts. If I try to
mod.js into Deno, it will only do what I ask it to do, and import
mod.js, but that means my code won't be as well type checked as if TypeScript
was considering the
mod.d.ts file in place of the
In order to support this in Deno, Deno has two solutions, of which there is a variation of a solution to enhance support. The two main situations you come across would be:
Providing types when importing
.d.ts file) or have otherwise obtained the types, you want to use, you can
@deno-types compiler hint.
@deno-types needs to be a single line
double slash comment, where when used impacts the next import or re-export
coolLib.js and I had a separate
coolLib.d.ts file that I wanted to use, I would import it like this:
// @deno-types="./coolLib.d.ts" import * as coolLib from "./coolLib.js";
When type checking
coolLib and your usage of it in the file, the
The pattern matching for the compiler hint is somewhat forgiving and will accept quoted and non-question values for the specifier as well as it accepts whitespace before and after the equals sign.
Providing types when hosting
If you are in control of the source code of the module, or you are in control of how the file is hosted on a web server, there are two ways to inform Deno of the types for a given module, without requiring the importer to do anything special.
Using the triple-slash reference directive
For example, if I had create
coolLib.js and along side of it I had created my
type definitions for my library in
coolLib.d.ts I could do the following in
When Deno encounters this directive, it would resolve the
Using X-TypeScript-Types header
Similar to the triple-slash directive, Deno supports a header for remote modules
that instructs Deno where to locate the types for a given module. For example, a
https://example.com/coolLib.js might look something like this:
When seeing this header, Deno would attempt to retrieve
https://example.com/coolLib.d.ts and use that when type checking the original
Type declaration semantics
Type declaration files (
.d.ts files) follow the same semantics as other files
in Deno. This means that declaration files are assumed to be module declarations
(UMD declarations) and not ambient/global declarations. It is unpredictable
how Deno will handle ambient/global declarations.
In addition, if a type declaration imports something else, like another
file, its resolution follow the normal import rules of Deno. For a lot of the
.d.ts files that are generated and available on the web, they may not be
compatible with Deno.
Deno Friendly CDNs
Skypack.dev is a CDN which provides type declarations (via the
X-TypeScript-Typesheader) when you append
?dtsas a query string to your remote module import statements. For example:
import React from "https://cdn.skypack.dev/react?dts";
checkJs set to
false (the default for Deno), the TypeScript
analysis on it, to at least try to determine the shape of the exports of that
module to validate the import in the TypeScript file.
This is usually never a problem when trying to import a "regular" ES module, but in some cases if the module has special packaging, or is a global UMD module, TypeScript's analysis of the module can fail and cause misleading errors. The best thing to do in this situation is provide some form of types using one of the methods mention above.
While it isn't required to understand how Deno works internally to be able to leverage TypeScript with Deno well, it can help to understand how it works.
Before any code is executed or compiled, Deno generates a module graph by parsing the root module, and then detecting all of its dependencies, and then retrieving and parsing those modules, recursively, until all the dependencies are retrieved.
For each dependency, there are two potential "slots" that are used. There is the
code slot and the type slot. As the module graph is filled out, if the module is
type only dependencies, like
.d.ts files fill the type slot.
This means when you import a
.d.ts module, or you use one of the solutions
provided to TypeScript instead when resolving the module.