跳至主要内容

Deno 中的 TypeScript 概述

Deno 的优势之一是它将 TypeScript 视为一等公民语言,就像 JavaScript 或 WebAssembly 一样,在 Deno 中运行代码时。这意味着您可以在 Deno 中运行或导入 TypeScript,而无需安装任何其他内容,只需 Deno CLI 即可。

等等,Deno 真的运行 TypeScript 吗? 你可能会问自己。嗯,这取决于你对“运行”的定义。有人可能会说,在浏览器中,你实际上并没有运行 JavaScript。浏览器中的 JavaScript 引擎将 JavaScript 转换为一系列操作码,然后在沙箱中执行这些操作码。因此,它将 JavaScript 转换为类似于汇编的东西。即使 WebAssembly 也经历了类似的转换,因为 WebAssembly 与架构无关,而它需要被转换为特定平台架构所需的机器特定操作码。因此,当我们说 TypeScript 是 Deno 中的一等公民语言时,我们的意思是,我们努力使 TypeScript 的编写和运行体验与 JavaScript 和 WebAssembly 一样简单直观。

在幕后,我们使用 Rust 和 JavaScript 中的一系列技术来提供这种体验。

它是如何工作的?

从高层次来看,Deno 将 TypeScript(以及 TSX 和 JSX)转换为 JavaScript。它通过结合 TypeScript 编译器(我们将其构建到 Deno 中)和一个名为 swc 的 Rust 库来实现这一点。当代码经过类型检查和转换后,它将被存储在缓存中,以便下次运行时无需再次将其从源代码转换为 JavaScript。

您可以通过运行 deno info 查看缓存位置。

> 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"

如果您查看该缓存,您将看到一个模拟源目录结构的目录结构,以及单独的 .js.meta 文件(也可能包含 .map 文件)。.js 文件是转换后的源文件,而 .meta 文件包含我们想要缓存的有关该文件元数据,目前包含源模块的哈希,这有助于我们管理缓存失效。您可能还会看到一个 .buildinfo 文件,它是一个 TypeScript 编译器增量构建信息文件,我们将其缓存以帮助加快类型检查速度。

类型检查

TypeScript 的主要优势之一是您可以使代码更具类型安全性,这样原本在语法上有效的 JavaScript 代码将变成带有关于“不安全”警告的 TypeScript 代码。

您可以使用以下命令对代码进行类型检查(无需执行):

deno check module.ts
# or also type check remote modules and npm packages
deno check --all module.ts

类型检查可能需要相当长的时间,尤其是在您正在处理一个正在进行大量更改的代码库时。我们已经尝试优化类型检查,但它仍然需要付出代价。因此,默认情况下,TypeScript 模块在执行之前不会进行类型检查。

deno run module.ts

使用上面的命令时,Deno 将在执行之前简单地转译模块,忽略任何潜在的类型相关问题。为了在执行发生之前执行模块的类型检查,必须将 --check 参数与 deno run 一起使用。

deno run --check module.ts
# or also type check remote modules and npm packages
deno run --check=all module.ts

虽然 tsc(默认情况下)在遇到诊断(类型检查)问题时仍然会发出 JavaScript 代码,但 Deno 目前将其视为终止性问题。当使用 deno run 以及 --check 参数时,类型相关的诊断将阻止程序运行:它将在这些警告处停止,并在执行代码之前退出进程。

为了避免这种情况,您需要解决问题,使用 // @ts-ignore// @ts-expect-error 注释,或者完全跳过类型检查。

您可以这里了解更多关于类型检查参数的信息。

确定文件类型

由于 Deno 支持 JavaScript、TypeScript、JSX、TSX 模块,因此 Deno 必须决定如何处理这些类型的文件。对于本地模块,Deno 完全根据扩展名来确定这一点。当本地文件缺少扩展名时,它被假定为 JavaScript。

对于远程模块,使用媒体类型(mime 类型)来确定模块的类型,其中模块的路径用于帮助影响文件类型,当文件类型不明确时。

例如,.d.ts 文件和 .ts 文件在 TypeScript 中具有不同的语义,并且在 Deno 中需要以不同的方式处理。虽然我们希望将 .ts 文件转换为 JavaScript,但 .d.ts 文件不包含任何“可运行”代码,它只是描述类型(通常是“普通”JavaScript)。因此,当我们获取远程模块时,.ts..d.ts 文件的媒体类型看起来相同。因此,我们查看路径,如果我们看到以 .d.ts 结尾的路径,我们将其视为仅类型定义文件,而不是“可运行”TypeScript。

支持的媒体类型

下表提供了 Deno 在识别远程模块的文件类型时支持的媒体类型列表

媒体类型文件处理方式
application/typescriptTypeScript(受路径扩展名影响)
text/typescriptTypeScript(受路径扩展名影响)
video/vnd.dlna.mpeg-ttsTypeScript(受路径扩展名影响)
video/mp2tTypeScript(受路径扩展名影响)
application/x-typescriptTypeScript(受路径扩展名影响)
application/javascriptJavaScript(受路径扩展名影响)
text/javascriptJavaScript(受路径扩展名影响)
application/ecmascriptJavaScript(受路径扩展名影响)
text/ecmascriptJavaScript(受路径扩展名影响)
application/x-javascriptJavaScript(受路径扩展名影响)
application/nodeJavaScript(受路径扩展名影响)
text/jsxJSX
text/tsxTSX
text/plain尝试确定路径扩展名,否则未知
application/octet-stream尝试确定路径扩展名,否则未知

默认情况下严格

Deno 默认情况下以严格模式对 TypeScript 进行类型检查,TypeScript 核心团队建议严格模式作为合理的默认值。此模式通常启用 TypeScript 的功能,这些功能可能应该从一开始就存在,但随着 TypeScript 的不断发展,对于现有代码来说将是重大更改。

混合 JavaScript 和 TypeScript

默认情况下,Deno 不会对 JavaScript 进行类型检查。这可以通过配置更改,详情请参考 在 Deno 中配置 TypeScript。Deno 支持在复杂场景下 JavaScript 导入 TypeScript 和 TypeScript 导入 JavaScript。

需要注意的是,当对 TypeScript 进行类型检查时,Deno 默认会“读取”所有 JavaScript 代码,以便评估它们对 TypeScript 类型的影响。类型检查器会尽力推断从 TypeScript 导入的 JavaScript 代码的类型,包括读取任何 JSDoc 注释。有关详细信息,请参考 类型和类型声明 部分。

类型解析

Deno 的核心设计原则之一是避免非标准模块解析,这同样适用于类型解析。如果您想使用带有类型定义的 JavaScript(例如 .d.ts 文件),您必须明确告诉 Deno。有关如何实现这一点的详细信息,请参考 类型和类型声明 部分。