deno.com
在本页中

配置 TypeScript

Deno 的灵活性体现在其对 TypeScript 和 JavaScript 的平等对待。无论您是从 JavaScript 过渡到 TypeScript,还是反之亦然,Deno 都具有简化此过程的功能。

类型检查 JavaScript 跳转到标题

您可能希望使您的 JavaScript 更加类型安全,而无需在各处添加类型注解。Deno 支持使用 TypeScript 类型检查器来类型检查 JavaScript。您可以通过将 check JavaScript 编译指示添加到文件来标记单个文件

// @ts-check

这将导致类型检查器推断有关 JavaScript 代码的类型信息,并引发任何问题作为诊断问题。

可以通过提供一个配置文件并将 check JS 选项设置为 true,为程序中的所有 JavaScript 文件启用这些选项,如下所示。然后在命令行运行时使用 --config 选项。

{
  "compilerOptions": {
    "checkJs": true
  }
}

在 JavaScript 中使用 JSDoc 跳转到标题

当类型检查 JavaScript 或将 JavaScript 导入到 TypeScript 中时,JSDoc 注解可以提供超出代码本身可以推断出的额外类型信息。如果您使用受支持的 TypeScript JSDoc 内联注解您的代码,Deno 可以无缝地支持这一点。

例如,要设置数组的类型,请使用以下 JSDoc 注释

/** @type {string[]} */
const a = [];

跳过类型检查 跳转到标题

您可能有正在试验的 TypeScript 代码,其中语法有效但并非完全类型安全。您可以通过传递 --no-check 标志来绕过整个程序的类型检查。

您还可以通过使用 nocheck 编译指示来跳过整个文件的类型检查,包括在启用了 check JS 的情况下跳过 JavaScript 文件

// @ts-nocheck

将 JS 文件重命名为 TS 文件 跳转到标题

TypeScript 文件受益于 TypeScript 编译器能够对您的代码进行更彻底的安全检查。这通常被称为严格模式。当您将 .js 文件重命名为 .ts 时,您可能会看到新的类型错误,而 TypeScript 以前无法检测到这些错误。

在 Deno 中配置 TypeScript 跳转到标题

TypeScript 提供了许多配置选项,如果您刚开始使用 TS,这些选项可能会让人望而生畏。Deno 旨在简化 TypeScript 的使用,而不是让您淹没在无数的设置中。Deno 配置 TypeScript 以使其开箱即用。无需额外的配置麻烦!

但是,如果您确实想更改 TypeScript 编译器选项,Deno 允许您在 deno.json 文件中执行此操作。在命令行上提供路径,或使用默认路径。例如

deno run --config ./deno.json main.ts

注意

如果您正在创建需要配置文件的库,请记住,您的 TS 模块的所有使用者也将需要该配置文件。此外,配置文件中可能存在使其他 TypeScript 模块不兼容的设置。

TS 编译器选项 跳转到标题

下表列出了可以更改的编译器选项、它们在 Deno 中的默认值以及有关该选项的任何其他说明

选项 默认值 备注
allowJs true 这几乎永远不需要更改
allowUnreachableCode false
allowUnusedLabels false
checkJs false 如果 true,则使 TypeScript 类型检查 JavaScript
jsx "react"
jsxFactory "React.createElement"
jsxFragmentFactory "React.Fragment"
keyofStringsOnly false
lib [ "deno.window" ] 此选项的默认值因 Deno 中的其他设置而异。如果提供了此选项,它将覆盖默认值。有关更多信息,请参见下文。
noErrorTruncation false
noFallthroughCasesInSwitch false
noImplicitAny true
noImplicitOverride true
noImplicitReturns false
noImplicitThis true
noImplicitUseStrict true
noStrictGenericChecks false
noUnusedLocals false
noUnusedParameters false
noUncheckedIndexedAccess false
reactNamespace React
strict true
strictBindCallApply true
strictFunctionTypes true
strictPropertyInitialization true
strictNullChecks true
suppressExcessPropertyErrors false
suppressImplicitAnyIndexErrors false
useUnknownInCatchVariables true

有关编译器选项及其如何影响 TypeScript 的完整列表,请参阅 TypeScript 手册

使用 “lib” 属性 跳转到标题

如果您正在开发一个将代码运送到多个运行时的项目,例如浏览器,您可以通过 compilerOptions 中的 “lib” 属性来调整默认类型。

用户感兴趣的内置库

  • "deno.ns" - 这包括所有自定义的 Deno 全局命名空间 API 以及 Deno 对 import.meta 的补充。这通常不应与其他库或全局类型冲突。
  • "deno.unstable" - 这包括额外的非稳定 Deno 全局命名空间 API。
  • "deno.window" - 这是检查 Deno 主运行时脚本时使用的“默认”库。它包括 "deno.ns" 以及 Deno 内置的扩展的其他类型库。此库将与 "dom""dom.iterable" 等标准 TypeScript 库冲突。
  • "deno.worker" - 这是检查 Deno Web Worker 脚本时使用的库。有关 Web Worker 的更多信息,请查看 类型检查 Web Worker
  • "dom.asynciterable" - TypeScript 当前不包括 Deno 实现的 DOM 异步可迭代对象(以及多个浏览器),因此我们自己实现了它,直到它在 TypeScript 中可用。

这些是默认情况下未启用但对于编写也旨在在另一个运行时中工作的代码很有用的常用库

  • "dom" - 随 TypeScript 附带的主要浏览器全局库。类型定义在许多方面与 "deno.window" 冲突,因此如果使用 "dom",请考虑仅使用 "deno.ns" 来公开 Deno 特定的 API。
  • "dom.iterable" - 浏览器全局库的可迭代扩展。
  • "scripthost" - Microsoft Windows 脚本主机的库。
  • "webworker" - 浏览器中 Web Worker 的主要库。与 "dom" 类似,这将与 "deno.window""deno.worker" 冲突,因此请考虑仅使用 "deno.ns" 来公开 Deno 特定的 API。
  • "webworker.importscripts" - 公开 Web Worker 中的 importScripts() API 的库。
  • "webworker.iterable" - 将可迭代对象添加到 Web Worker 中的对象的库。现代浏览器支持此功能。

以 Deno 和浏览器为目标 跳转到标题

您可能想要编写在 Deno 和浏览器中无缝运行的代码。在这种情况下,您需要在使用任何特定于一方或另一方的 API 之前有条件地检查执行环境。在这种情况下,典型的 compilerOptions 配置可能如下所示

deno.json
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"]
  }
}

这应该允许 Deno 正确类型检查大多数代码。

如果您希望在 Deno 中使用 --unstable 标志运行代码,那么您也应该将该库添加到组合中

deno.json
{
  "compilerOptions": {
    "lib": [
      "dom",
      "dom.iterable",
      "dom.asynciterable",
      "deno.ns",
      "deno.unstable"
    ]
  }
}

通常,当您在 TypeScript 中使用 "lib" 选项时,您还需要包含一个 “es” 库。在 "deno.ns""deno.unstable" 的情况下,当您引入它们时,它们会自动包含 "esnext"

注意

如果您遇到类似 找不到 documentHTMLElement 的类型错误,则可能是您正在使用的库依赖于 DOM。这对于设计为在浏览器和服务器端运行的软件包很常见。默认情况下,Deno 仅包含直接支持的库。假设软件包在运行时正确识别它正在运行的环境,则使用 DOM 库类型检查代码是“安全”的。

类型和类型声明 跳转到标题

Deno 应用了无非标准模块解析的设计原则。当 TypeScript 检查文件时,它只关注其类型。相比之下,tsc 编译器采用复杂的逻辑来解析这些类型。默认情况下,tsc 期望带有扩展名的模糊模块说明符(例如,.ts.d.ts.js)。但是,Deno 处理显式说明符。

有趣的是:假设您要使用一个已经转译为 JavaScript 的 TypeScript 文件,以及它的类型定义文件(mod.jsmod.d.ts)。如果您将 mod.js 导入到 Deno 中,它会严格按照您的请求导入 JavaScript 文件。但这里有一个问题:您的代码不会像 TypeScript 同时考虑 mod.d.ts 文件和 mod.js 文件那样进行彻底的类型检查。

为了解决这个问题,Deno 提供了两种解决方案,每种方案都适用于特定的场景

作为导入者:如果您知道哪些类型应应用于 JavaScript 模块,则可以通过显式指定类型来增强类型检查。

作为提供者:如果您是模块的提供者或托管者,则每个使用者都将受益,而无需担心类型解析。

在导入时提供类型 跳转到标题

如果您正在使用 JavaScript 模块,并且您已经创建了类型(.d.ts 文件)或以其他方式获得了要使用的类型,则可以使用 @ts-types 编译器提示指示 Deno 在类型检查时使用该文件,而不是 JavaScript 文件。

例如,如果您有一个 JavaScript 模块 coolLib.js 和一个单独的 coolLib.d.ts 文件,您将像这样导入它

// @ts-types="./coolLib.d.ts"
import * as coolLib from "./coolLib.js";

当您对 coolLib 执行类型检查并在您的文件中使用它时,来自 coolLib.d.ts 的 TypeScript 类型定义将优先于检查 JavaScript 文件。

注意

过去,@ts-types 指令称为 @deno-types。此别名仍然有效,但不建议再使用。请使用 @ts-types

在托管时提供类型 跳转到标题

如果您可以控制模块的源代码或文件在 Web 服务器上的托管方式,则有两种方法可以让 Deno 知道特定模块的类型(这不需要导入者执行任何特殊操作)。

@ts-self-types 跳转到标题

如果您提供 JavaScript 文件,并且想要提供一个包含此文件类型的声明文件,则可以在 JS 文件中指定 @ts-self-types 指令,指向声明文件。

例如,如果您制作了一个 coolLib.js 库,并在 coolLib.d.ts 中编写了其类型定义,则 ts-self-types 指令将如下所示

coolLib.js
// @ts-self-types="./coolLib.d.ts"

// ... the rest of the JavaScript ...

X-TypeScript-Types 跳转到标题

Deno 支持远程模块的标头,该标头指示 Deno 在哪里找到给定模块的类型。例如,对 https://example.com/coolLib.js 的响应可能如下所示

HTTP/1.1 200 OK
Content-Type: application/javascript; charset=UTF-8
Content-Length: 648
X-TypeScript-Types: ./coolLib.d.ts

当看到此标头时,Deno 将尝试检索 https://example.com/coolLib.d.ts 并在类型检查原始模块时使用它。

使用环境或全局类型 跳转到标题

总的来说,最好在 Deno 中使用模块/UMD 类型定义,其中模块明确导入它所依赖的类型。模块化类型定义可以通过类型定义中的 declare global 表达 全局范围的增强。例如

declare global {
  var AGlobalString: string;
}

这将使 AGlobalString 在导入类型定义时在全局命名空间中可用。

但在某些情况下,当利用其他现有类型库时,可能无法利用模块化类型定义。因此,有一些方法可以在类型检查程序时包含任意类型定义。

三斜线指令 跳转到标题

此选项将类型定义与代码本身耦合。通过在 TS 文件(不是 JS 文件!)中,在模块类型附近添加三斜线 types 指令,类型检查文件将包括类型定义。例如

/// <reference types="./types.d.ts" />

提供的说明符的解析方式与 Deno 中的任何其他说明符一样,这意味着它需要扩展名,并且相对于引用它的模块。它也可以是完全限定的 URL

/// <reference types="https://deno.land/x/pkg@1.0.0/types.d.ts" />

在 deno.json 中提供 “types” 跳转到标题

另一种选择是在 deno.jsoncompilerOptions 中提供 "types" 值。例如

deno.json
{
  "compilerOptions": {
    "types": [
      "./types.d.ts",
      "https://deno.land/x/pkg@1.0.0/types.d.ts",
      "/Users/me/pkg/types.d.ts"
    ]
  }
}

与上面的三斜线引用类似,"types" 数组中提供的说明符将像 Deno 中的其他说明符一样解析。在相对说明符的情况下,它将相对于配置文件的路径解析。确保通过指定 --config=path/to/file 标志来告诉 Deno 使用此文件。

类型检查 Web Worker 跳转到标题

当 Deno 在 Web Worker 中加载 TypeScript 模块时,它将自动针对 Deno Web Worker 库类型检查该模块及其依赖项。这在其他上下文中(如 deno check 或编辑器中)可能会带来挑战。有两种方法可以指示 Deno 使用 Worker 库而不是标准 Deno 库。

三斜线指令 跳转到标题

此选项将库设置与代码本身耦合。通过在 Worker 脚本的入口点文件顶部附近添加以下三斜线指令,Deno 现在将将其类型检查为 Deno Worker 脚本,而不管模块的分析方式如何

/// <reference no-default-lib="true" />
/// <reference lib="deno.worker" />

第一个指令确保不使用其他默认库。如果省略此指令,您将获得一些冲突的类型定义,因为 Deno 也会尝试应用标准 Deno 库。第二个指令指示 Deno 应用内置的 Deno Worker 类型定义以及依赖库(如 "esnext")。

这种方法的一个缺点是,它使代码更难移植到其他非 Deno 平台(如 tsc),因为它只有 Deno 内置了 "deno.worker" 库。

在 deno.json 中提供 “lib” 设置 跳转到标题

您可以在 deno.json 文件中提供 “lib” 选项,以指示 Deno 使用库文件。例如

deno.json
{
  "compilerOptions": {
    "target": "esnext",
    "lib": ["deno.worker"]
  }
}

然后,当运行 deno 子命令时,您需要传递 --config path/to/file 参数,或者如果您使用的是利用 Deno 语言服务器的 IDE,请设置 deno.config 设置。

如果您还有非 Worker 脚本,您要么需要省略 --config 参数,要么使用一个配置为满足您的非 Worker 脚本需求的参数。

要点 跳转到标题

类型声明语义 跳转到标题

类型声明文件(.d.ts 文件)遵循与 Deno 中其他文件相同的语义。这意味着声明文件被假定为模块声明(UMD 声明),而不是环境/全局声明。Deno 如何处理环境/全局声明是不可预测的。

此外,如果类型声明导入了其他内容,例如另一个 .d.ts 文件,则其解析遵循 Deno 的正常导入规则。对于 Web 上生成和可用的许多 .d.ts 文件,它们可能与 Deno 不兼容。

esm.sh 是一个 CDN,默认情况下提供类型声明(通过 X-TypeScript-Types 标头)。可以通过将 ?no-dts 附加到导入 URL 来禁用它

import React from "https://esm.sh/react?no-dts";

类型检查时 JavaScript 的行为 跳转到标题

当您在 Deno 中将 JavaScript 代码导入到 TypeScript 中时,即使您已将 checkJs 设置为 false(这是 Deno 的默认行为),TypeScript 编译器仍会分析 JavaScript 模块。它尝试从该模块推断导出的形状,以验证 TypeScript 文件中的导入。

通常,当导入标准 ES 模块时,这不是问题。但是,在某些情况下,TypeScript 的分析可能会失败,例如,对于具有特殊打包或全局 UMD(通用模块定义)模块的模块。当面临这种情况时,最好的方法是使用前面提到的方法之一提供某种形式的类型信息。

内部原理 跳转到标题

虽然不需要了解 Deno 的内部工作原理也能很好地利用 Deno 中的 TypeScript,但了解它的工作原理可能会有所帮助。

在执行或编译任何代码之前,Deno 通过解析根模块来生成模块图,然后检测其所有依赖项,然后递归地检索和解析这些模块,直到检索到所有依赖项。

对于每个依赖项,都有两个可能的“槽”被使用。有代码槽和类型槽。随着模块图的填充,如果模块是或可以发出到 JavaScript 的内容,它会填充代码槽,而仅类型依赖项(如 .d.ts 文件)会填充类型槽。

构建模块图后,当需要类型检查图时,Deno 启动 TypeScript 编译器,并将需要可能作为 JavaScript 发出的模块的名称提供给它。在此过程中,TypeScript 编译器将请求其他模块,Deno 将查看依赖项的槽,如果类型槽已填充,则先提供类型槽,然后再提供代码槽。

这意味着当您导入 .d.ts 模块,或者您使用上述解决方案之一为 JavaScript 代码提供替代类型模块时,这才是解析模块时提供给 TypeScript 的内容。

您找到所需的信息了吗?

隐私政策