mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 00:59:24 -05:00
parent
973c33c899
commit
02c6a88d8a
12 changed files with 918 additions and 407 deletions
|
@ -75,6 +75,7 @@ pub const IGNORED_COMPILER_OPTIONS: &[&str] = &[
|
|||
"inlineSources",
|
||||
"module",
|
||||
"noEmitHelpers",
|
||||
"noErrorTruncation",
|
||||
"noLib",
|
||||
"noResolve",
|
||||
"outDir",
|
||||
|
|
|
@ -8,6 +8,5 @@ In this chapter we'll discuss:
|
|||
- [Writing our own script](./getting_started/first_steps.md)
|
||||
- [Command line interface](./getting_started/command_line_interface.md)
|
||||
- [Understanding permissions](./getting_started/permissions.md)
|
||||
- [Using Deno with TypeScript](./getting_started/typescript.md)
|
||||
- [Using WebAssembly](./getting_started/webassembly.md)
|
||||
- [Debugging your code](./getting_started/debugging_your_code.md)
|
||||
|
|
|
@ -1,183 +1,4 @@
|
|||
## Using TypeScript
|
||||
|
||||
<!-- TODO(lucacasonato): text on 'just import .ts' -->
|
||||
|
||||
Deno supports both JavaScript and TypeScript as first class languages at
|
||||
runtime. This means it requires fully qualified module names, including the
|
||||
extension (or a server providing the correct media type). In addition, Deno has
|
||||
no "magical" module resolution. Instead, imported modules are specified as files
|
||||
(including extensions) or fully qualified URL imports. Typescript modules can be
|
||||
directly imported. E.g.
|
||||
|
||||
```ts
|
||||
import { Response } from "https://deno.land/std@$STD_VERSION/http/server.ts";
|
||||
import { queue } from "./collections.ts";
|
||||
```
|
||||
|
||||
### `--no-check` option
|
||||
|
||||
When using `deno run`, `deno test`, `deno cache`, or `deno bundle` you can
|
||||
specify the `--no-check` flag to disable TypeScript type checking. This can
|
||||
significantly reduce the time that program startup takes. This can be very
|
||||
useful when type checking is provided by your editor and you want startup time
|
||||
to be as fast as possible (for example when restarting the program automatically
|
||||
with a file watcher).
|
||||
|
||||
To make the most of skipping type checks, `--no-check` transpiles each module in
|
||||
isolation without using information from imported modules. This maximizes
|
||||
potential for concurrency and incremental rebuilds. On the other hand, the
|
||||
transpiler cannot know if `export { Foo } from "./foo.ts"` should be preserved
|
||||
(in case `Foo` is a value) or removed (in case `Foo` is strictly a type). To
|
||||
resolve such ambiguities, Deno enforces
|
||||
[`isolatedModules`](https://www.typescriptlang.org/tsconfig#isolatedModules) on
|
||||
all TS code. This means that `Foo` in the above example must be a value, and the
|
||||
[`export type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-exports)
|
||||
syntax must be used instead if `Foo` is a type.
|
||||
|
||||
Another consequence of `isolatedModules` is that the type-directed `const enum`
|
||||
is treated like `enum`. The legacy `import =` and `export =` syntaxes are also
|
||||
not supported by `--no-check`.
|
||||
|
||||
### Using external type definitions
|
||||
|
||||
The out of the box TypeScript compiler though relies on both extension-less
|
||||
modules and the Node.js module resolution logic to apply types to JavaScript
|
||||
modules.
|
||||
|
||||
In order to bridge this gap, Deno supports three ways of referencing type
|
||||
definition files without having to resort to "magic" resolution.
|
||||
|
||||
#### Compiler hint
|
||||
|
||||
If you are importing a JavaScript module, and you know where the type definition
|
||||
for that module is located, you can specify the type definition at import. This
|
||||
takes the form of a compiler hint. Compiler hints inform Deno the location of
|
||||
`.d.ts` files and the JavaScript code that is imported that they relate to. The
|
||||
hint is `@deno-types` and when specified the value will be used in the compiler
|
||||
instead of the JavaScript module. For example, if you had `foo.js`, but you know
|
||||
that alongside of it was `foo.d.ts` which was the types for the file, the code
|
||||
would look like this:
|
||||
|
||||
```ts
|
||||
// @deno-types="./foo.d.ts"
|
||||
import * as foo from "./foo.js";
|
||||
```
|
||||
|
||||
The value follows the same resolution logic as importing a module, meaning the
|
||||
file needs to have an extension and is relative to the current module. Remote
|
||||
specifiers are also allowed.
|
||||
|
||||
The hint affects the next `import` statement (or `export ... from` statement)
|
||||
where the value of the `@deno-types` will be substituted at compile time instead
|
||||
of the specified module. Like in the above example, the Deno compiler will load
|
||||
`./foo.d.ts` instead of `./foo.js`. Deno will still load `./foo.js` when it runs
|
||||
the program.
|
||||
|
||||
#### Triple-slash reference directive in JavaScript files
|
||||
|
||||
If you are hosting modules which you want to be consumed by Deno, and you want
|
||||
to inform Deno about the location of the type definitions, you can utilize a
|
||||
triple-slash directive in the actual code. For example, if you have a JavaScript
|
||||
module and you would like to provide Deno with the location of the type
|
||||
definition which happens to be alongside that file, your JavaScript module named
|
||||
`foo.js` might look like this:
|
||||
|
||||
```js
|
||||
/// <reference types="./foo.d.ts" />
|
||||
export const foo = "foo";
|
||||
```
|
||||
|
||||
Deno will see this, and the compiler will use `foo.d.ts` when type-checking the
|
||||
file, though `foo.js` will be loaded at runtime. The resolution of the value of
|
||||
the directive follows the same resolution logic as importing a module, meaning
|
||||
the file needs to have an extension and is relative to the current file. Remote
|
||||
specifiers are also allowed.
|
||||
|
||||
#### X-TypeScript-Types custom header
|
||||
|
||||
If you are hosting modules which you want to be consumed by Deno, and you want
|
||||
to inform Deno the location of the type definitions, you can use a custom HTTP
|
||||
header of `X-TypeScript-Types` to inform Deno of the location of that file.
|
||||
|
||||
The header works in the same way as the triple-slash reference mentioned above,
|
||||
it just means that the content of the JavaScript file itself does not need to be
|
||||
modified, and the location of the type definitions can be determined by the
|
||||
server itself.
|
||||
|
||||
**Not all type definitions are supported.**
|
||||
|
||||
Deno will use the compiler hint to load the indicated `.d.ts` files, but some
|
||||
`.d.ts` files contain unsupported features. Specifically, some `.d.ts` files
|
||||
expect to be able to load or reference type definitions from other packages
|
||||
using the module resolution logic. For example a type reference directive to
|
||||
include `node`, expecting to resolve to some path like
|
||||
`./node_modules/@types/node/index.d.ts`. Since this depends on non-relative
|
||||
"magical" resolution, Deno cannot resolve this.
|
||||
|
||||
**Why not use the triple-slash type reference in TypeScript files?**
|
||||
|
||||
The TypeScript compiler supports triple-slash directives, including a type
|
||||
reference directive. If Deno used this, it would interfere with the behavior of
|
||||
the TypeScript compiler. Deno only looks for the directive in JavaScript (and
|
||||
JSX) files.
|
||||
|
||||
### Custom TypeScript Compiler Options
|
||||
|
||||
In the Deno ecosystem, all strict flags are enabled in order to comply with
|
||||
TypeScript's ideal of being `strict` by default. However, in order to provide a
|
||||
way to support customization a configuration file such as `tsconfig.json` might
|
||||
be provided to Deno on program execution.
|
||||
|
||||
You need to explicitly tell Deno where to look for this configuration by setting
|
||||
the `-c` (or `--config`) argument when executing your application.
|
||||
|
||||
```shell
|
||||
deno run -c tsconfig.json mod.ts
|
||||
```
|
||||
|
||||
Following are the currently allowed settings and their default values in Deno:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": false,
|
||||
"allowUmdGlobalAccess": false,
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"alwaysStrict": true,
|
||||
"assumeChangesOnlyAffectDirectDependencies": false,
|
||||
"checkJs": false,
|
||||
"disableSizeLimit": false,
|
||||
"generateCpuProfile": "profile.cpuprofile",
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
"lib": [],
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitUseStrict": false,
|
||||
"noStrictGenericChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"preserveConstEnums": false,
|
||||
"removeComments": false,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"suppressExcessPropertyErrors": false,
|
||||
"suppressImplicitAnyIndexErrors": false,
|
||||
"useDefineForClassFields": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For documentation on allowed values and use cases please visit the
|
||||
[typescript docs](https://www.typescriptlang.org/docs/handbook/compiler-options.html).
|
||||
|
||||
**Note**: Any options not listed above are either not supported by Deno or are
|
||||
listed as deprecated/experimental in the TypeScript documentation.
|
||||
> ℹ️ This section has been moved to
|
||||
> [Using TypeScript Chapter](../typescript.md).
|
||||
|
|
|
@ -1,225 +1,4 @@
|
|||
## Compiler APIs
|
||||
|
||||
> This API is unstable. Learn more about
|
||||
> [unstable features](../runtime/stability.md).
|
||||
|
||||
Deno supports runtime access to the built-in TypeScript compiler. There are
|
||||
three methods in the `Deno` namespace that provide this access.
|
||||
|
||||
### `Deno.compile()`
|
||||
|
||||
This works similar to `deno cache` in that it can fetch and cache the code,
|
||||
compile it, but not run it. It takes up to three arguments, the `rootName`,
|
||||
optionally `sources`, and optionally `options`. The `rootName` is the root
|
||||
module which will be used to generate the resulting program. This is like the
|
||||
module name you would pass on the command line in
|
||||
`deno run --reload example.ts`. The `sources` is a hash where the key is the
|
||||
fully qualified module name, and the value is the text source of the module. If
|
||||
`sources` is passed, Deno will resolve all the modules from within that hash and
|
||||
not attempt to resolve them outside of Deno. If `sources` are not provided, Deno
|
||||
will resolve modules as if the root module had been passed on the command line.
|
||||
Deno will also cache any of these resources. All resolved resources are treated
|
||||
as dynamic imports and require read or net permissions depending on if they're
|
||||
local or remote. The `options` argument is a set of options of type
|
||||
`Deno.CompilerOptions`, which is a subset of the TypeScript compiler options
|
||||
containing the ones supported by Deno.
|
||||
|
||||
The method resolves with a tuple. The first argument contains any diagnostics
|
||||
(syntax or type errors) related to the code. The second argument is a map where
|
||||
the keys are the output filenames and the values are the content.
|
||||
|
||||
An example of providing sources:
|
||||
|
||||
```ts
|
||||
const [diagnostics, emitMap] = await Deno.compile("/foo.ts", {
|
||||
"/foo.ts": `import * as bar from "./bar.ts";\nconsole.log(bar);\n`,
|
||||
"/bar.ts": `export const bar = "bar";\n`,
|
||||
});
|
||||
|
||||
assert(diagnostics == null); // ensuring no diagnostics are returned
|
||||
console.log(emitMap);
|
||||
```
|
||||
|
||||
We would expect map to contain 4 "files", named `/foo.js.map`, `/foo.js`,
|
||||
`/bar.js.map`, and `/bar.js`.
|
||||
|
||||
When not supplying resources, you can use local or remote modules, just like you
|
||||
could do on the command line. So you could do something like this:
|
||||
|
||||
```ts
|
||||
const [diagnostics, emitMap] = await Deno.compile(
|
||||
"https://deno.land/std@$STD_VERSION/examples/welcome.ts",
|
||||
);
|
||||
```
|
||||
|
||||
In this case `emitMap` will contain a `console.log()` statement.
|
||||
|
||||
### `Deno.bundle()`
|
||||
|
||||
This works a lot like `deno bundle` does on the command line. It is also like
|
||||
`Deno.compile()`, except instead of returning a map of files, it returns a
|
||||
single string, which is a self-contained JavaScript ES module which will include
|
||||
all of the code that was provided or resolved as well as exports of all the
|
||||
exports of the root module that was provided. It takes up to three arguments,
|
||||
the `rootName`, optionally `sources`, and optionally `options`. The `rootName`
|
||||
is the root module which will be used to generate the resulting program. This is
|
||||
like module name you would pass on the command line in `deno bundle example.ts`.
|
||||
The `sources` is a hash where the key is the fully qualified module name, and
|
||||
the value is the text source of the module. If `sources` is passed, Deno will
|
||||
resolve all the modules from within that hash and not attempt to resolve them
|
||||
outside of Deno. If `sources` are not provided, Deno will resolve modules as if
|
||||
the root module had been passed on the command line. All resolved resources are
|
||||
treated as dynamic imports and require read or net permissions depending if
|
||||
they're local or remote. Deno will also cache any of these resources. The
|
||||
`options` argument is a set of options of type `Deno.CompilerOptions`, which is
|
||||
a subset of the TypeScript compiler options containing the ones supported by
|
||||
Deno.
|
||||
|
||||
An example of providing sources:
|
||||
|
||||
```ts
|
||||
const [diagnostics, emit] = await Deno.bundle("/foo.ts", {
|
||||
"/foo.ts": `import * as bar from "./bar.ts";\nconsole.log(bar);\n`,
|
||||
"/bar.ts": `export const bar = "bar";\n`,
|
||||
});
|
||||
|
||||
assert(diagnostics == null); // ensuring no diagnostics are returned
|
||||
console.log(emit);
|
||||
```
|
||||
|
||||
We would expect `emit` to be the text for an ES module, which would contain the
|
||||
output sources for both modules.
|
||||
|
||||
When not supplying resources, you can use local or remote modules, just like you
|
||||
could do on the command line. So you could do something like this:
|
||||
|
||||
```ts
|
||||
const [diagnostics, emit] = await Deno.bundle(
|
||||
"https://deno.land/std@$STD_VERSION/http/server.ts",
|
||||
);
|
||||
```
|
||||
|
||||
In this case `emit` will be a self contained JavaScript ES module with all of
|
||||
its dependencies resolved and exporting the same exports as the source module.
|
||||
|
||||
### `Deno.transpileOnly()`
|
||||
|
||||
This is based off of the TypeScript function `transpileModule()`. All this does
|
||||
is "erase" any types from the modules and emit JavaScript. There is no type
|
||||
checking and no resolution of dependencies. It accepts up to two arguments, the
|
||||
first is a hash where the key is the module name and the value is the content.
|
||||
The only purpose of the module name is when putting information into a source
|
||||
map, of what the source file name was. The second argument contains optional
|
||||
`options` of the type `Deno.CompilerOptions`. The function resolves with a map
|
||||
where the key is the source module name supplied, and the value is an object
|
||||
with a property of `source` and optionally `map`. The first is the output
|
||||
contents of the module. The `map` property is the source map. Source maps are
|
||||
provided by default, but can be turned off via the `options` argument.
|
||||
|
||||
An example:
|
||||
|
||||
```ts
|
||||
const result = await Deno.transpileOnly({
|
||||
"/foo.ts": `enum Foo { Foo, Bar, Baz };\n`,
|
||||
});
|
||||
|
||||
console.log(result["/foo.ts"].source);
|
||||
console.log(result["/foo.ts"].map);
|
||||
```
|
||||
|
||||
We would expect the `enum` would be rewritten to an IIFE which constructs the
|
||||
enumerable, and the map to be defined.
|
||||
|
||||
### Referencing TypeScript library files
|
||||
|
||||
When you use `deno run`, or other Deno commands which type check TypeScript,
|
||||
that code is evaluated against custom libraries which describe the environment
|
||||
that Deno supports. By default, the compiler runtime APIs which type check
|
||||
TypeScript also use these libraries (`Deno.compile()` and `Deno.bundle()`).
|
||||
|
||||
But if you want to compile or bundle TypeScript for some other runtime, you may
|
||||
want to override the default libraries. To do this, the runtime APIs support the
|
||||
`lib` property in the compiler options. For example, if you had TypeScript code
|
||||
that is destined for the browser, you would want to use the TypeScript `"dom"`
|
||||
library:
|
||||
|
||||
```ts
|
||||
const [errors, emitted] = await Deno.compile(
|
||||
"main.ts",
|
||||
{
|
||||
"main.ts": `document.getElementById("foo");\n`,
|
||||
},
|
||||
{
|
||||
lib: ["dom", "esnext"],
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
For a list of all the libraries that TypeScript supports, see the
|
||||
[`lib` compiler option](https://www.typescriptlang.org/docs/handbook/compiler-options.html)
|
||||
documentation.
|
||||
|
||||
**Don't forget to include the JavaScript library**
|
||||
|
||||
Just like `tsc`, when you supply a `lib` compiler option, it overrides the
|
||||
default ones, which means that the basic JavaScript library won't be included
|
||||
and you should include the one that best represents your target runtime (e.g.
|
||||
`es5`, `es2015`, `es2016`, `es2017`, `es2018`, `es2019`, `es2020` or `esnext`).
|
||||
|
||||
#### Including the `Deno` namespace
|
||||
|
||||
In addition to the libraries that are provided by TypeScript, there are four
|
||||
libraries that are built into Deno that can be referenced:
|
||||
|
||||
- `deno.ns` - Provides the `Deno` namespace.
|
||||
- `deno.shared_globals` - Provides global interfaces and variables which Deno
|
||||
supports at runtime that are then exposed by the final runtime library.
|
||||
- `deno.window` - Exposes the global variables plus the Deno namespace that are
|
||||
available in the Deno main worker and is the default for the runtime compiler
|
||||
APIs.
|
||||
- `deno.worker` - Exposes the global variables that are available in workers
|
||||
under Deno.
|
||||
|
||||
So to add the Deno namespace to a compilation, you would include the `deno.ns`
|
||||
lib in the array. For example:
|
||||
|
||||
```ts
|
||||
const [errors, emitted] = await Deno.compile(
|
||||
"main.ts",
|
||||
{
|
||||
"main.ts": `document.getElementById("foo");\n`,
|
||||
},
|
||||
{
|
||||
lib: ["dom", "esnext", "deno.ns"],
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
**Note** that the Deno namespace expects a runtime environment that is at least
|
||||
ES2018 or later. This means if you use a lib "lower" than ES2018 you will get
|
||||
errors logged as part of the compilation.
|
||||
|
||||
#### Using the triple slash reference
|
||||
|
||||
You do not have to specify the `lib` in the compiler options. Deno also supports
|
||||
[the triple-slash reference to a lib](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-lib-)
|
||||
which can be embedded in the contents of the file. For example, if you have a
|
||||
`main.ts` like:
|
||||
|
||||
```ts
|
||||
/// <reference lib="dom" />
|
||||
|
||||
document.getElementById("foo");
|
||||
```
|
||||
|
||||
It would compile without errors like this:
|
||||
|
||||
```ts
|
||||
const [errors, emitted] = await Deno.compile("./main.ts", undefined, {
|
||||
lib: ["esnext"],
|
||||
});
|
||||
```
|
||||
|
||||
**Note** that the `dom` library conflicts with some of the default globals that
|
||||
are defined in the default type library for Deno. To avoid this, you need to
|
||||
specify a `lib` option in the compiler options to the runtime compiler APIs.
|
||||
> ℹ️ This section has been moved to
|
||||
> [TypeScript Runtime APIs](../typescript/runtime.md).
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"first_steps": "First steps",
|
||||
"command_line_interface": "Command line interface",
|
||||
"permissions": "Permissions",
|
||||
"typescript": "Using TypeScript",
|
||||
"webassembly": "Using WebAssembly",
|
||||
"debugging_your_code": "Debugging your code"
|
||||
}
|
||||
|
@ -21,7 +20,6 @@
|
|||
"stability": "Stability",
|
||||
"program_lifecycle": "Program lifecycle",
|
||||
"permission_apis": "Permission APIs",
|
||||
"compiler_apis": "Compiler APIs",
|
||||
"web_platform_apis": "Web Platform APIs",
|
||||
"location_api": "Location API",
|
||||
"workers": "Workers"
|
||||
|
@ -36,6 +34,17 @@
|
|||
"import_maps": "Import maps"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"name": "Using TypeScript",
|
||||
"children": {
|
||||
"overview": "Overview",
|
||||
"configuration": "Configuration",
|
||||
"types": "Types and type declarations",
|
||||
"migration": "Migrating to/from JavaScript",
|
||||
"runtime": "Runtime compiler APIs",
|
||||
"faqs": "Frequently asked questions"
|
||||
}
|
||||
},
|
||||
"standard_library": {
|
||||
"name": "Standard library"
|
||||
},
|
||||
|
|
10
docs/typescript.md
Normal file
10
docs/typescript.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Using TypeScript
|
||||
|
||||
In this chapter we will discuss:
|
||||
|
||||
- [Overview of TypeScript in Deno](./typescript/overview.md)
|
||||
- [Configuring TypeScript in Deno](./typescript/configuration.md)
|
||||
- [Types and Type Declarations](./typescript/types.md)
|
||||
- [Migrating to/from JavaScript](./typescript/migration.md)
|
||||
- [Runtime compiler APIs](./typescript/runtime.md)
|
||||
- [FAQs about TypeScript in Deno](./typescript/faqs.md)
|
110
docs/typescript/configuration.md
Normal file
110
docs/typescript/configuration.md
Normal file
|
@ -0,0 +1,110 @@
|
|||
## Configuring TypeScript in Deno
|
||||
|
||||
TypeScript comes with a load of different options that can be configured, but
|
||||
Deno strives to make it easy to use TypeScript with Deno. Lots of different
|
||||
options frustrates that goal. To make things easier, Deno configures TypeScript
|
||||
to "just work" and shouldn't require additional configuration.
|
||||
|
||||
That being said, Deno does support using a TypeScript configuration file, though
|
||||
like the rest of Deno, the detection and use of use of a configuration file is
|
||||
not automatic. To use a TypeScript configuration file with Deno, you have to
|
||||
provide a path on the command line. For example:
|
||||
|
||||
```
|
||||
> deno run --config ./tsconfig.json main.ts
|
||||
```
|
||||
|
||||
> ⚠️ Do consider though that if you are creating libraries that require a
|
||||
> configuration file, all of the consumers of your modules will require that
|
||||
> configuration file too if you distribute your modules as TypeScript. In
|
||||
> addition, there could be settings you do in the configuration file that make
|
||||
> other TypeScript modules incompatible. Honestly it is best to use the Deno
|
||||
> defaults and to think long and hard about using a configuration file.
|
||||
|
||||
### How Deno uses a configuration file
|
||||
|
||||
Deno does not process a TypeScript configuration file like `tsc` does, as there
|
||||
are lots of parts of a TypeScript configuration file that are meaningless in a
|
||||
Deno context or would cause Deno to not function properly if they were applied.
|
||||
|
||||
Deno only looks at the `compilerOptions` section of a configuration file, and
|
||||
even then it only considers certain compiler options, with the rest being
|
||||
ignored.
|
||||
|
||||
Here is a table of compiler options that can be changed, their default in Deno
|
||||
and any other notes about that option:
|
||||
|
||||
| Option | Default | Notes |
|
||||
| -------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `allowJs` | `true` | This almost never needs to be changed |
|
||||
| `allowUnreachableCode` | `false` | |
|
||||
| `allowUnusedLabels` | `false` | |
|
||||
| `checkJs` | `false` | If `true` causes TypeScript to type check JavaScript |
|
||||
| `experimentalDecorators` | `true` | We enable these by default as they are already opt-in in the code and when we skip type checking, the Rust based emitter has them on by default. We strongly discourage the use of legacy decorators, as they are incompatible with the future decorators standard in JavaScript |
|
||||
| `jsx` | `"react"` | |
|
||||
| `jsxFactory` | `"React.createElement"` | |
|
||||
| `jsxFragmentFactory` | `"React.Fragment"` | |
|
||||
| `keysofStringsOnly` | `false` | |
|
||||
| `lib` | `[ "deno.window" ]` | The default for this varies based on other settings in Deno. If it is supplied, it overrides the default. See below for more information. |
|
||||
| `noFallthroughCasesInSwitch` | `false` | |
|
||||
| `noImplicitAny` | `true` | |
|
||||
| `noImplicitReturns` | `false` | |
|
||||
| `noImplicitThis` | `true` | |
|
||||
| `noImplicitUseStrict` | `true` | |
|
||||
| `noStrictGenericChecks` | `false` | |
|
||||
| `noUnusedLocals` | `false` | |
|
||||
| `noUnusedParameters` | `false` | |
|
||||
| `reactNamespace` | `React` | |
|
||||
| `strict` | `true` | |
|
||||
| `strictBindApply` | `true` | |
|
||||
| `strictFunctionTypes` | `true` | |
|
||||
| `strictPropertyInitialization` | `true` | |
|
||||
| `strictNullChecks` | `true` | |
|
||||
| `suppressExcessPropertyErrors` | `false` | |
|
||||
| `suppressImplicitAnyIndexErrors` | `false` | |
|
||||
|
||||
For a full list of compiler options and how they affect TypeScript, please refer
|
||||
to the
|
||||
[TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/compiler-options.html)
|
||||
|
||||
### What an implied tsconfig.json looks like
|
||||
|
||||
It is impossible to get `tsc` to behave like Deno. It is also difficult to get
|
||||
the TypeScript language service to behave like Deno. This is why we have built a
|
||||
language service directly into Deno. That being said, it can be useful to
|
||||
understand what is implied.
|
||||
|
||||
If you were to write a `tsconfig.json` for Deno, it would look something like
|
||||
this:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"inlineSourceMap": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react",
|
||||
"lib": ["deno.window"],
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"target": "esnext"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can't copy paste this into a `tsconfig.json` and get it to work,
|
||||
specifically because of the built in type libraries that are custom to Deno
|
||||
which are provided to the TypeScript compiler. This can somewhat be mocked by
|
||||
running `deno types` on the command line and piping the output to a file and
|
||||
including that in the files as part of the program, removing the `"lib"` option,
|
||||
and setting the `"noLib"` option to `true`.
|
||||
|
||||
If you use the `--unstable` flag, Deno will change the `"lib"` option to
|
||||
`[ "deno.window", "deno.unstable" ]`. If you are trying to load a worker, that
|
||||
is type checked with `"deno.worker"` instead of `"deno.window"`.
|
||||
|
||||
### Using the "lib" property
|
||||
|
||||
[TBC]
|
116
docs/typescript/faqs.md
Normal file
116
docs/typescript/faqs.md
Normal file
|
@ -0,0 +1,116 @@
|
|||
## FAQs about TypeScript in Deno
|
||||
|
||||
### Can I use TypeScript not written for Deno?
|
||||
|
||||
Maybe. That is the best answer, we are afraid. For lots of reasons, Deno has
|
||||
chosen to have fully qualified module specifiers. In part this is because it
|
||||
treats TypeScript as a first class language. Also, Deno uses explicit module
|
||||
resolution, with no _magic_. This is effectively the same way browsers
|
||||
themselves work, thought they don't obviously support TypeScript directly. If
|
||||
the TypeScript modules use imports that don't have these design decisions in
|
||||
mind, they may not work under Deno.
|
||||
|
||||
Also, in recent versions of Deno (starting with 1.5), we have started to use a
|
||||
Rust library to do transformations of TypeScript to JavaScript in certain
|
||||
scenarios. Because of this, there are certain situations in TypeScript where
|
||||
type information is required, and therefore those are not supported under Deno.
|
||||
If you are using `tsc` as stand-alone, the setting to use is `"isolatedModules"`
|
||||
and setting it to `true` to help ensure that your code can be properly handled
|
||||
by Deno.
|
||||
|
||||
One of the ways to deal with the extension and the lack of _magical_ resolution
|
||||
is to use [import maps](../linking_to_external_code/import_maps.md) which would
|
||||
allow you to specify "packages" of bare specifiers which then Deno could resolve
|
||||
and load.
|
||||
|
||||
### What version(s) of TypeScript does Deno support?
|
||||
|
||||
Deno is built with a specific version of TypeScript. To find out what this is,
|
||||
type the following on the command line:
|
||||
|
||||
```shell
|
||||
> deno --version
|
||||
```
|
||||
|
||||
The TypeScript version (along with the version of Deno and v8) will be printed.
|
||||
Deno tries to keep up to date with general releases of TypeScript, providing
|
||||
them in the next patch or minor release of Deno.
|
||||
|
||||
### There was a breaking change in the version of TypeScript that Deno uses, why did you break my program?
|
||||
|
||||
We do not consider changes in behavior or breaking changes in TypeScript
|
||||
releases as breaking changes for Deno. TypeScript is a generally mature language
|
||||
and breaking changes in TypeScript are almost always "good things" making code
|
||||
more sound, and it is best that we all keep our code sound. If there is a
|
||||
blocking change in the version of TypeScript and it isn't suitable to use an
|
||||
older release of Deno until the problem can be resolved, then you should be able
|
||||
to use `--no-check` to skip type checking all together.
|
||||
|
||||
In addition you can utilize `@ts-ignore` to _ignore_ a specific error in code
|
||||
that you control. You can also replace whole dependencies, using
|
||||
[import maps](../linking_to_external_code/import_maps), for situations where a
|
||||
dependency of a dependency isn't being maintained or has some sort of breaking
|
||||
change you want to bypass while waiting for it to be updated.
|
||||
|
||||
### Why are you forcing me to use isolated modules, why can't I use const enums with Deno, why do I need to do export type?
|
||||
|
||||
As of Deno 1.5 we defaulted to _isolatedModules_ to `true` and in Deno 1.6 we
|
||||
removed the options to set it back to `false` via a configuration file. The
|
||||
_isolatedModules_ option forces the TypeScript compiler to check and emit
|
||||
TypeScript as if each module would stand on its own. TypeScript has a few _type
|
||||
directed emits_ in the language at the moment. While not allowing type directed
|
||||
emits into the language was a design goal for TypeScript, it has happened
|
||||
anyways. This means that the TypeScript compiler needs to understand the
|
||||
erasable types in the code to determine what to emit, which when you are trying
|
||||
to make a fully erasable type system on top of JavaScript, that becomes a
|
||||
problem.
|
||||
|
||||
When people started transpiling TypeScript without `tsc`, these type directed
|
||||
emits became a problem, since the likes of Babel simply try to erase the types
|
||||
without needing to understand the types to direct the emit. In the internals of
|
||||
Deno we have started to use a Rust based emitter which allows us to optionally
|
||||
skip type checking and generates the bundles for things like `deno bundle`. Like
|
||||
all transpilers, it doesn't care about the types, it just tries to erase them.
|
||||
This means in certain situations we cannot support those type directed emits.
|
||||
|
||||
So instead of trying to get every user to understand when and how we could
|
||||
support the type directed emits, we made the decision to disable the use of them
|
||||
by forcing the _isolatedModules_ option to `true`. This means that even when we
|
||||
are using the TypeScript compiler to emit the code, it will follow the same
|
||||
"rules" that the Rust based emitter follows.
|
||||
|
||||
This means that certain language features are not supportable. Those features
|
||||
are:
|
||||
|
||||
- Re-exporting of types is ambigious and requires to know if the source module
|
||||
is exporting runtime code or just type information. Therefore, it is
|
||||
recommended that you use `import type` and `export type` for type only imports
|
||||
and exports. This will help ensure that when the code is emitted, that all the
|
||||
types are erased.
|
||||
- `const enum` is not supported. `const enum`s require type information to
|
||||
direct the emit, as `const enum`s get written out as hard coded values.
|
||||
Especially when `const enum`s get exported, they are a type system only
|
||||
construct.
|
||||
- `export =` and `import =` are legacy TypeScript syntax which we do not
|
||||
support.
|
||||
- Only `declare namespace` is support. Runtime `namespace` is legacy TypeScript
|
||||
syntax that is not supported.
|
||||
|
||||
### Why don't you support language service plugins or transformer plugins?
|
||||
|
||||
While `tsc` supports language service plugins, Deno does not. Deno does not
|
||||
always use the built in TypeScript compiler to do what it does, and the
|
||||
complexity of adding support for a language service plugin is not feasible.
|
||||
TypeScript does not support emitter plugins, but there are a few community
|
||||
projects which _hack_ emitter plugins into TypeScript. First, we wouldn't want
|
||||
to support something that TypeScript doesn't support, plus we do not always use
|
||||
the TypeScript compiler for the emit, which would mean we would need to ensure
|
||||
we supported it in all modes, and the other emitter is written in Rust, meaning
|
||||
that any emitter plugin for TypeScript wouldn't be available for the Rust
|
||||
emitter.
|
||||
|
||||
The TypeScript in Deno isn't intended to be a fully flexible TypeScript
|
||||
compiler. Its main purpose is to ensure that TypeScript and JavaScript can run
|
||||
under Deno. The secondary ability to do TypeScript and JavaScript emitting via
|
||||
the runtime API `Deno.emit()` is intended to be simple and straight forward and
|
||||
support a certain set of use cases.
|
71
docs/typescript/migration.md
Normal file
71
docs/typescript/migration.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
## Migrating to and from JavaScript
|
||||
|
||||
One of the advantages of Deno is that it treats TypeScript and JavaScript pretty
|
||||
equally. This might mean that transitioning from JavaScript to TypeScript or
|
||||
even from TypeScript to JavaScript is something you want to accomplish. There
|
||||
are several features of Deno that can help with this.
|
||||
|
||||
### Type checking JavaScript
|
||||
|
||||
You might have some JavaScript that you would like to ensure is more type sound
|
||||
but you don't want to go through a process of adding type annotations
|
||||
everywhere.
|
||||
|
||||
Deno supports using the TypeScript type checker to type check JavaScript. You
|
||||
can mark any individual file by adding the check JavaScript pragma to the file:
|
||||
|
||||
```js
|
||||
// @ts-check
|
||||
```
|
||||
|
||||
This will cause the type checker to infer type information about the JavaScript
|
||||
code and raise any issues as diagnostic issues.
|
||||
|
||||
These can be turned on for all JavaScript files in a program by providing a
|
||||
configuration file with the check JS option enabled:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And setting the `--config` option on the command line.
|
||||
|
||||
### Using JSDoc in JavaScript
|
||||
|
||||
If you are type checking JavaScript, or even importing JavaScript into
|
||||
TypeScript you can use JSDoc in JavaScript to express more types information
|
||||
than can just be inferred from the code itself. Deno supports this without any
|
||||
additional configuration, you simply need to annotate the code in line with the
|
||||
supported
|
||||
[TypeScript JSDoc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html).
|
||||
For example to set the type of an array:
|
||||
|
||||
```js
|
||||
/** @type {string[]} */
|
||||
const a = [];
|
||||
```
|
||||
|
||||
### Skipping type checking
|
||||
|
||||
You might have TypeScript code that you are experimenting with, where the syntax
|
||||
is valid but not fully type safe. You can always bypass type checking for a
|
||||
whole program by passing the `--no-check`.
|
||||
|
||||
You can also skip whole files being type checked, including JavaScript if you
|
||||
have check JS enabled, by using the no-check pragma:
|
||||
|
||||
```js
|
||||
// @ts-nocheck
|
||||
```
|
||||
|
||||
### Just renaming JS files to TS files
|
||||
|
||||
While this might work in some cases, it has some severe limits in Deno. This is
|
||||
because Deno, by default, runs type checking in what is called _strict mode_.
|
||||
This means a lot of unclear or ambiguous situations where are not caught in
|
||||
non-strict mode will result in diagnostics being generated, and JavaScript is
|
||||
nothing but unclear and ambiguous when it comes to types.
|
159
docs/typescript/overview.md
Normal file
159
docs/typescript/overview.md
Normal file
|
@ -0,0 +1,159 @@
|
|||
## Overview of TypeScript in Deno
|
||||
|
||||
One of the benefits of Deno is that it treats TypeScript as a first class
|
||||
language, just like JavaScript or Web Assembly, when running code in Deno. What
|
||||
that means is you can run or import TypeScript without installing anything more
|
||||
than the Deno CLI.
|
||||
|
||||
_But wait a minute, does Deno really run TypeScript?_ you might be asking
|
||||
yourself. Well, depends on what you mean by run. One could argue that in a
|
||||
browser you don't actually _run_ JavaScript either. The JavaScript engine in the
|
||||
browser translates the JavaScript to a series of operation codes, which it then
|
||||
executes in a sandbox. So it translates JavaScript to something close to
|
||||
assembly. Even Web Assembly goes through a similar translation, in that Web
|
||||
Assembly is architecture agnostic while it needs to be translated into the
|
||||
machine specific operation codes needed for the particular platform architecture
|
||||
it is running on. So when we say TypeScript is a first class language in Deno,
|
||||
we mean that we try to make the user experience in authoring and running
|
||||
TypeScript as easy and straightforward as JavaScript and Web Assembly.
|
||||
|
||||
Behind the scenes, we use a combination of technologies, in Rust and JavaScript,
|
||||
to provide that experience.
|
||||
|
||||
### How does it work?
|
||||
|
||||
At a high level, Deno converts TypeScript (as well as TSX and JSX) into
|
||||
JavaScript. It does this via a combination of the
|
||||
[TypeScript compiler](https://github.com/microsoft/TypeScript), which we build
|
||||
into Deno, and a Rust library called [swc](https://swc.rs/). When the code has
|
||||
been type checked and transformed, it is stored in a cache, ready for the next
|
||||
run without the need to convert it from its source to JavaScript again.
|
||||
|
||||
You can see this cache location by running `deno info`:
|
||||
|
||||
```shell
|
||||
> deno info
|
||||
DENO_DIR location: "/path/to/cache/deno"
|
||||
Remote modules cache: "/path/to/cache/deno/deps"
|
||||
TypeScript compiler cache: "/path/to/cache/deno/gen"
|
||||
```
|
||||
|
||||
If you were to look in that cache, you would see a directory structure that
|
||||
mimics that source directory structure and individual `.js` and `.meta` files
|
||||
(also potentially `.map` files). The `.js` file is the transformed source file
|
||||
while the `.meta` file contains meta data we want to cache about the file, which
|
||||
at the moment contains a _hash_ of the source module that helps us manage cache
|
||||
invalidation. You might also see a `.buildinfo` file as well, which is a
|
||||
TypeScript compiler incremental build information file, which we cache to help
|
||||
speed up type checking.
|
||||
|
||||
### Type Checking
|
||||
|
||||
One of the main advantages of TypeScript is that you can make code more type
|
||||
safe, so that what would be syntactically valid JavaScript becomes TypeScript
|
||||
with warnings about being "unsafe".
|
||||
|
||||
In Deno we handle TypeScript in two major ways. We can type check TypeScript,
|
||||
the default, or you can opt into skipping that checking using the `--no-check`
|
||||
flag. For example if you had a program you wanted to run, normally you would do
|
||||
something like this:
|
||||
|
||||
```
|
||||
deno run --allow-net my_server.ts
|
||||
```
|
||||
|
||||
But if you wanted to skip the type checking, you would do something like this:
|
||||
|
||||
```
|
||||
deno run --allow-net --no-check my_server.ts
|
||||
```
|
||||
|
||||
Type checking can take a significant amount of time, especially if you are
|
||||
working on a code base where you are making a lot of changes. We have tried to
|
||||
optimise the type checking, but it still comes at a cost. If you just want to
|
||||
hack at some code, or if you are working in an IDE which is type checking your
|
||||
code as you author it, using `--no-check` can certainly speed up the process of
|
||||
running TypeScript in Deno.
|
||||
|
||||
### Determining the type of file
|
||||
|
||||
Since Deno supports JavaScript, TypeScript, JSX, TSX modules, Deno has to make a
|
||||
decision about how to treat each of these kinds of files. For local modules,
|
||||
Deno makes this determination based fully on the extension. When the extension
|
||||
is absent in a local file, it is assumed to be JavaScript.
|
||||
|
||||
For remote modules, the media type (mime-type) is used to determine the type of
|
||||
the module, where the path of the module is used to help influence the file
|
||||
type, when it is ambiguous what type of file it is.
|
||||
|
||||
For example, a `.d.ts` file and a `.ts` file have different semantics in
|
||||
TypeScript as well as have different ways they need to be handled in Deno. While
|
||||
we expect to convert a `.ts` file into JavaScript, a `.d.ts` file contains no
|
||||
"runnable" code, and is simply describing types (often of "plain" JavaScript).
|
||||
So when we fetch a remote module, the media type for a `.ts.` and `.d.ts` file
|
||||
looks the same. So we look at the path, and if we see something that has a path
|
||||
that ends with `.d.ts` we treat it as a type definition only file instead of
|
||||
"runnable" TypeScript.
|
||||
|
||||
#### Supported media types
|
||||
|
||||
The following table provides a list of media types which Deno supports when
|
||||
identifying the type of file of a remote module:
|
||||
|
||||
| Media Type | How File is Handled |
|
||||
| -------------------------- | ----------------------------------------------------------- |
|
||||
| `application/typescript` | TypeScript (with path extension influence) |
|
||||
| `text/typescript` | TypeScript (with path extension influence) |
|
||||
| `video/vnd.dlna.mpeg-tts` | TypeScript (with path extension influence) |
|
||||
| `video/mp2t` | TypeScript (with path extension influence) |
|
||||
| `application/x-typescript` | TypeScript (with path extension influence) |
|
||||
| `application/javascript` | JavaScript (with path extensions influence) |
|
||||
| `text/javascript` | JavaScript (with path extensions influence) |
|
||||
| `application/ecmascript` | JavaScript (with path extensions influence) |
|
||||
| `text/ecmascript` | JavaScript (with path extensions influence) |
|
||||
| `application/x-javascript` | JavaScript (with path extensions influence) |
|
||||
| `application/node` | JavaScript (with path extensions influence) |
|
||||
| `text/jsx` | JSX |
|
||||
| `text/tsx` | TSX |
|
||||
| `text/plain` | Attempt to determine that path extension, otherwise unknown |
|
||||
| `application/octet-stream` | Attempt to determine that path extension, otherwise unknown |
|
||||
|
||||
### Strict by default
|
||||
|
||||
Deno type checks TypeScript in _strict_ mode by default, and the TypeScript core
|
||||
team recommends _strict_ mode as a sensible default. This mode generally enables
|
||||
features of TypeScript that probably should have been there from the start, but
|
||||
as TypeScript continued to evolve, would be breaking changes for existing code.
|
||||
|
||||
### Mixing JavaScript and TypeScript
|
||||
|
||||
By default, Deno does not type check JavaScript. This can be changed, and is
|
||||
discussed further in [Configuring TypeScript in Deno](./configuration.md). Deno
|
||||
does support JavaScript importing TypeScript and TypeScript import JavaScript,
|
||||
in complex scenarios.
|
||||
|
||||
An important note though is that when type checking TypeScript, by default Deno
|
||||
will "read" all the JavaScript in order to be able to evaluate how it might have
|
||||
an impact on the TypeScript types. The type checker will do the best it can to
|
||||
figure out what the types are of the JavaScript you import into TypeScript,
|
||||
including reading any JSDoc comments. Details of this are discussed in detail in
|
||||
the [Types and type declarations](./types.md) section.
|
||||
|
||||
### Diagnostics are terminal
|
||||
|
||||
While `tsc` by default will still emit JavaScript when run while encountering
|
||||
diagnostic (type checking) issues, Deno currently treats them as terminal. It
|
||||
will halt on these warnings, not cache any of the emitted files, and exit the
|
||||
process.
|
||||
|
||||
In order to avoid this, you will either need to resolve the issue, utilise the
|
||||
`// @ts-ignore` or `// @ts-expect-error` pragmas, or utilise `--no-check` to
|
||||
bypass type checking all together.
|
||||
|
||||
### Type resolution
|
||||
|
||||
One of the core design principles of Deno is to avoid "magical" resolution, and
|
||||
this applies to type resolution as well. If you want to utilise JavaScript that
|
||||
has type definitions (e.g. a `.d.ts` file), you have to explicitly tell Deno
|
||||
about this. The details of how this is accomplished are covered in the
|
||||
[Types and type declarations](./types.md) section.
|
268
docs/typescript/runtime.md
Normal file
268
docs/typescript/runtime.md
Normal file
|
@ -0,0 +1,268 @@
|
|||
## Runtime compiler APIs
|
||||
|
||||
> ⚠️ The runtime compiler API is unstable (and requires the `--unstable` flag to
|
||||
> be used to enable it).
|
||||
|
||||
The runtime compiler API allows access to the internals of Deno to be able to
|
||||
type check, transpile and bundle JavaScript and TypeScript. As of Deno 1.7,
|
||||
several disparate APIs we consolidated into a single API, `Deno.emit()`.
|
||||
|
||||
### Deno.emit()
|
||||
|
||||
The API is defined in the `Deno` namespace as:
|
||||
|
||||
```ts
|
||||
function emit(
|
||||
rootSpecifier: string | URL,
|
||||
options?: EmitOptions,
|
||||
): Promise<EmitResult>;
|
||||
```
|
||||
|
||||
The emit options are defined in the `Deno` namespace as:
|
||||
|
||||
```ts
|
||||
interface EmitOptions {
|
||||
/** Indicate that the source code should be emitted to a single file
|
||||
* JavaScript bundle that is an ES module (`"esm"`). */
|
||||
bundle?: "esm";
|
||||
/** If `true` then the sources will be typed checked, returning any
|
||||
* diagnostic errors in the result. If `false` type checking will be
|
||||
* skipped. Defaults to `true`.
|
||||
*
|
||||
* *Note* by default, only TypeScript will be type checked, just like on
|
||||
* the command line. Use the `compilerOptions` options of `checkJs` to
|
||||
* enable type checking of JavaScript. */
|
||||
check?: boolean;
|
||||
/** A set of options that are aligned to TypeScript compiler options that
|
||||
* are supported by Deno. */
|
||||
compilerOptions?: CompilerOptions;
|
||||
/** An [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps)
|
||||
* which will be applied to the imports. */
|
||||
importMap?: ImportMap;
|
||||
/** An absolute path to an [import-map](https://deno.land/manual/linking_to_external_code/import_maps#import-maps).
|
||||
* Required to be specified if an `importMap` is specified to be able to
|
||||
* determine resolution of relative paths. If a `importMap` is not
|
||||
* specified, then it will assumed the file path points to an import map on
|
||||
* disk and will be attempted to be loaded based on current runtime
|
||||
* permissions.
|
||||
*/
|
||||
importMapPath?: string;
|
||||
/** A record of sources to use when doing the emit. If provided, Deno will
|
||||
* use these sources instead of trying to resolve the modules externally. */
|
||||
sources?: Record<string, string>;
|
||||
}
|
||||
```
|
||||
|
||||
The emit result is defined in the `Deno` namespace as:
|
||||
|
||||
```ts
|
||||
interface EmitResult {
|
||||
/** Diagnostic messages returned from the type checker (`tsc`). */
|
||||
diagnostics: Diagnostic[];
|
||||
/** Any emitted files. If bundled, then the JavaScript will have the
|
||||
* key of `deno:///bundle.js` with an optional map (based on
|
||||
* `compilerOptions`) in `deno:///bundle.js.map`. */
|
||||
files: Record<string, string>;
|
||||
/** An optional array of any compiler options that were ignored by Deno. */
|
||||
ignoredOptions?: string[];
|
||||
/** An array of internal statistics related to the emit, for diagnostic
|
||||
* purposes. */
|
||||
stats: Array<[string, number]>;
|
||||
}
|
||||
```
|
||||
|
||||
The API is designed to support several use cases, which are described in the
|
||||
sections below.
|
||||
|
||||
### Using external sources
|
||||
|
||||
Using external sources, both local and remote, `Deno.emit()` can behave like
|
||||
`deno cache` does on the command line, resolving those external dependencies,
|
||||
type checking those dependencies, and providing an emitted output.
|
||||
|
||||
By default, `Deno.emit()` will utilise external resources. The _rootSpecifier_
|
||||
supplied as the first argument will determine what module will be used as the
|
||||
root. The root module is similar to what you would provide on the command line.
|
||||
|
||||
For example if you did:
|
||||
|
||||
```
|
||||
> deno run mod.ts
|
||||
```
|
||||
|
||||
You could do something similar with `Deno.emit()`:
|
||||
|
||||
```ts
|
||||
try {
|
||||
const { files } = await Deno.emit("mod.ts");
|
||||
for (const [fileName, text] of Object.entries(files)) {
|
||||
console.log(`emitted ${fileName} with a length of ${text.length}`);
|
||||
}
|
||||
} catch (e) {
|
||||
// something went wrong, inspect `e` to determine
|
||||
}
|
||||
```
|
||||
|
||||
`Deno.emit()` will use the same on disk cache for remote modules that the
|
||||
standard CLI does, and it inherits the permissions and cache options of the
|
||||
process that executes it.
|
||||
|
||||
If the _rootSpecifier_ is a relative path, then the current working directory of
|
||||
the Deno process will be used to resolve the specifier. (Not relative to the
|
||||
current module!)
|
||||
|
||||
The _rootSpecifier_ can be a string file path, a string URL, or a URL.
|
||||
`Deno.emit()` supports the same protocols for URLs that Deno supports, which are
|
||||
currently `file`, `http`, `https`, and `data`.
|
||||
|
||||
### Providing sources
|
||||
|
||||
Instead of resolving modules externally, you can provide `Deno.emit()` with the
|
||||
sources directly. This is especially useful for a server to be able to provide
|
||||
_on demand_ compiling of code supplied by a user, where the Deno process has
|
||||
collected all the code it wants to emit.
|
||||
|
||||
The sources are passed in the _sources_ property of the `Deno.emit()` _options_
|
||||
argument:
|
||||
|
||||
```ts
|
||||
const { files } = await Deno.emit("/mod.ts", {
|
||||
sources: {
|
||||
"/mod.ts": `import * as a from "./a.ts";\nconsole.log(a);\n`,
|
||||
"/a.ts": `export const a: Record<string, string> = {};\n`,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When sources are provided, Deno will no longer look externally and will try to
|
||||
resolve all modules from within the map of sources provided, though the module
|
||||
resolution follow the same rules as if the modules were external. For example
|
||||
all module specifiers need their full filename. Also, because there are no media
|
||||
types, if you are providing remote URLs in the sources, the path should end with
|
||||
the appropriate extension, so that Deno can determine how to handle the file.
|
||||
|
||||
### Type checking and emitting
|
||||
|
||||
By default, `Deno.emit()` will type check any TypeScript (and TSX) it
|
||||
encounters, just like on the command line. It will also attempt to transpile
|
||||
JSX, but will leave JavaScript "alone". This behavior can be changed by changing
|
||||
the compiler options. For example if you wanted Deno to type check your
|
||||
JavaScript as well, you could set the _checkJs_ option to `true` in the compiler
|
||||
options:
|
||||
|
||||
```ts
|
||||
const { files, diagnostics } = await Deno.emit("./mod.js", {
|
||||
compilerOptions: {
|
||||
checkJs: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The `Deno.emit()` result provides any diagnostic messages about the code
|
||||
supplied. On the command line, any diagnostic messages get logged to stderr and
|
||||
the Deno process terminates, but with `Deno.emit()` they are returned to the
|
||||
caller.
|
||||
|
||||
Typically you will want to check if there are any diagnostics and handle them
|
||||
appropriately. You can introspect the diagnostics individually, but there is a
|
||||
handy formatting function available to make it easier to potentially log the
|
||||
diagnostics to the console for the user called `Deno.formatDiagnostics()`:
|
||||
|
||||
```ts
|
||||
const { files, diagnostics } = await Deno.emit("./mod.ts");
|
||||
if (diagnostics.length) {
|
||||
// there is something that impacted the emit
|
||||
console.warn(Deno.formatDiagnostics(diagnostics));
|
||||
}
|
||||
```
|
||||
|
||||
### Bundling
|
||||
|
||||
`Deno.emit()` is also capable of providing output similar to `deno bundle` on
|
||||
the command line. This is enabled by setting the _bundle_ option to `"esm"`.
|
||||
(Currently Deno only supports bundling as a single file ES module, but there are
|
||||
plans to add support for an IIFE bundle format as well):
|
||||
|
||||
```ts
|
||||
const { files, diagnostics } = await Deno.emit("./mod.ts", {
|
||||
bundle: "esm",
|
||||
});
|
||||
```
|
||||
|
||||
The _files_ of the result will contain a single key named `deno:///bundle.js` of
|
||||
which the value with be the resulting bundle.
|
||||
|
||||
> ⚠️ Just like with `deno bundle`, the bundle will not include things like
|
||||
> dynamic imports or worker scripts, and those would be expected to be resolved
|
||||
> and available when the code is run.
|
||||
|
||||
### Import maps
|
||||
|
||||
`Deno.emit()` supports import maps as well, just like on the command line. This
|
||||
is a really powerful feature that can be used even more effectively to emit and
|
||||
bundle code.
|
||||
|
||||
Because of the way import maps work, when using with `Deno.emit()` you also have
|
||||
to supply an absolute URL for the import map. This allows Deno to resolve any
|
||||
relative URLs specified in the import map. This needs to be supplied even if the
|
||||
import map doesn't contain any relative URLs. The URL does not need to really
|
||||
exist, it is just feed to the API.
|
||||
|
||||
An example might be that I want to use a bare specifier to load a special
|
||||
version of _lodash_ I am using with my project. I could do the following:
|
||||
|
||||
```ts
|
||||
const { files } = await Deno.emit("mod.ts", {
|
||||
bundle: "esm",
|
||||
importMap: {
|
||||
imports: {
|
||||
"lodash": "https://deno.land/x/lodash",
|
||||
},
|
||||
},
|
||||
importMapPath: "file:///import-map.json",
|
||||
});
|
||||
```
|
||||
|
||||
> ⚠️ If you are not bundling your code, the emitted code specifiers do not get
|
||||
> rewritten, that means that whatever process will consume the code, Deno or a
|
||||
> browser for example, would need to support import maps and have that map
|
||||
> available at runtime.
|
||||
|
||||
### Skip type checking/transpiling only
|
||||
|
||||
`Deno.emit()` supports skipping type checking similar to the `--no-check` flag
|
||||
on the command line. This is accomplished by setting the _check_ property to
|
||||
`false`:
|
||||
|
||||
```ts
|
||||
const { files } = await Deno.emit("./mod.ts", {
|
||||
check: false,
|
||||
});
|
||||
```
|
||||
|
||||
Setting _check_ to `false` will instruct Deno to not utilise the TypeScript
|
||||
compiler to type check the code and emit it, instead only transpiling the code
|
||||
from within Deno. This can be significantly quicker than doing the full type
|
||||
checking.
|
||||
|
||||
### Compiler options
|
||||
|
||||
`Deno.emit()` support quite a few compiler options that can impact how code is
|
||||
type checked and emitted. They are similar to the options supported by a
|
||||
`tsconfig.json` in the `compilerOptions` section, but there are several options
|
||||
that are not supported. This is because they are either meaningless in Deno our
|
||||
would cause Deno to not be able to work properly. The defaults for `Deno.emit()`
|
||||
are the same defaults that are on the command line. The options are
|
||||
[documented here](https://doc.deno.land/builtin/unstable#Deno.CompilerOptions)
|
||||
along with their default values and are built into the Deno types.
|
||||
|
||||
If you are type checking your code, the compiler options will be type checked
|
||||
for you, but if for some reason you are either dynamically providing the
|
||||
compiler options or are not type checking, then the result of `Deno.emit()` will
|
||||
provide you with an array of _ignoredOptions_ if there are any.
|
||||
|
||||
> ⚠️ we have only tried to disable/remove options that we know won't work, that
|
||||
> does not mean we extensively test all options in all configurations under
|
||||
> `Deno.emit()`. You may find that some behaviors do not match what you can get
|
||||
> from `tsc` or are otherwise incompatible. If you do find something that
|
||||
> doesn't work, please do feel free to raise an issue.
|
168
docs/typescript/types.md
Normal file
168
docs/typescript/types.md
Normal file
|
@ -0,0 +1,168 @@
|
|||
## 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 `.js`
|
||||
(plus a whole other set of logic when the module resolution is set to `"node"`).
|
||||
Deno deals with explicit specifiers.
|
||||
|
||||
This can cause a couple problems though. For example, let's say I want to
|
||||
consume a TypeScript file that has already been transpiled to JavaScript along
|
||||
with a type definition file. So I have `mod.js` and `mod.d.ts`. If I try to
|
||||
import `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 `mod.js` file.
|
||||
|
||||
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:
|
||||
|
||||
- As the importer of a JavaScript module, I know what types should be applied to
|
||||
the module.
|
||||
- As the supplier of the JavaScript module, I know what types should be applied
|
||||
to the module.
|
||||
|
||||
The latter case is the better case, meaning you as the provider or host of the
|
||||
module, everyone can consume it without having to figure out how to resolve the
|
||||
types for the JavaScript module, but when consuming modules that you may not
|
||||
have direct control over, the ability to do the former is also required.
|
||||
|
||||
### Providing types when importing
|
||||
|
||||
If you are consuming a JavaScript module and you have either created types (a
|
||||
`.d.ts` file) or have otherwise obtained the types, you want to use, you can
|
||||
instruct Deno to use that file when type checking instead of the JavaScript file
|
||||
using the `@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
|
||||
statement.
|
||||
|
||||
For example if I have a JavaScript modules `coolLib.js` and I had a separate
|
||||
`coolLib.d.ts` file that I wanted to use, I would import it like this:
|
||||
|
||||
```ts
|
||||
// @deno-types="./coolLib.d.ts"
|
||||
import * as coolLib from "./coolLib.js";
|
||||
```
|
||||
|
||||
When type checking `coolLib` and your usage of it in the file, the
|
||||
`coolLib.d.ts` types will be used instead of looking at the JavaScript file.
|
||||
|
||||
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
|
||||
|
||||
Deno supports using the triple-slash reference directive, which adopts the
|
||||
reference comment used by TypeScript in TypeScript files to _include_ other
|
||||
files and applies it to JavaScript files.
|
||||
|
||||
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
|
||||
the `coolLib.js` file:
|
||||
|
||||
```js
|
||||
/// <reference path="./coolLib.d.ts" />
|
||||
|
||||
// ... the rest of the JavaScript ...
|
||||
```
|
||||
|
||||
When Deno encounters this directive, it would resolve the `./coolLib.d.ts` file
|
||||
and use that instead of the JavaScript file when TypeScript was type checking
|
||||
the file, but still load the JavaScript file when running the program.
|
||||
|
||||
#### 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
|
||||
response for `https://example.com/coolLib.js` might look something like this:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/javascript; charset=UTF-8
|
||||
Content-Length: 648
|
||||
X-TypeScript-Types: ./coolLib.d.ts
|
||||
```
|
||||
|
||||
When seeing this header, Deno would attempt to retrieve
|
||||
`https://example.com/coolLib.d.ts` and use that when type checking the original
|
||||
module.
|
||||
|
||||
### Important points
|
||||
|
||||
#### 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 `.d.ts`
|
||||
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.
|
||||
|
||||
To overcome this problem, some solution providers, like the
|
||||
[Skypack CDN](https://www.skypack.dev/), will automatically bundle type
|
||||
declarations just like they provide bundles of JavaScript as ESM.
|
||||
|
||||
#### Deno Friendly CDNs
|
||||
|
||||
There are CDNs which host JavaScript modules that integrate well with Deno.
|
||||
|
||||
- [Skypack.dev](https://docs.skypack.dev/skypack-cdn/code/deno) is a CDN which
|
||||
provides type declarations (via the `X-TypeScript-Types` header) when you
|
||||
append `?dts` as a query string to your remote module import statements. For
|
||||
example:
|
||||
|
||||
```ts
|
||||
import React from "https://cdn.skypack.dev/react?dts";
|
||||
```
|
||||
|
||||
### Behavior of JavaScript when type checking
|
||||
|
||||
If you import JavaScript into TypeScript in Deno and there are no types, even if
|
||||
you have `checkJs` set to `false` (the default for Deno), the TypeScript
|
||||
compiler will still access the JavaScript module and attempt to do some static
|
||||
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.
|
||||
|
||||
#### Internals
|
||||
|
||||
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
|
||||
something that is or can be emitted to JavaScript, it fills the code slot, and
|
||||
type only dependencies, like `.d.ts` files fill the type slot.
|
||||
|
||||
When the module graph is built, and there is a need to type check the graph,
|
||||
Deno starts up the TypeScript compiler and feeds it the names of the modules
|
||||
that need to be potentially emitted as JavaScript. During that process, the
|
||||
TypeScript compiler will request additional modules, and Deno will look at the
|
||||
slots for the dependency, offering it the type slot if it is filled before
|
||||
offering it the code slot.
|
||||
|
||||
This means when you import a `.d.ts` module, or you use one of the solutions
|
||||
above to provide alternative type modules for JavaScript code, that is what is
|
||||
provided to TypeScript instead when resolving the module.
|
Loading…
Reference in a new issue