Deno的设计原则之一是没有神奇的解决方案。当tyescript对文件进行类型检查时,它只关心文件的类型,而TSC编译器有很多逻辑来尝试解析这些类型。默认情况下,它需要带有扩展名的模棱两可的模块说明符,并尝试在.ts说明符下查找文件,然后是.d.ts,最后是.js(当模块解析设置为“node”时,再加上另一组完整的逻辑)。Deno处理显式说明符。
不过,这可能会导致一些问题。例如,假设我想使用一个已经转换为JavaScript的类型脚本文件和一个类型定义文件。所以我有mod.js和mod.d.ts。如果我尝试将mod.js导入Deno,它将只执行我要求它做的事情,并导入mod.js,但这意味着我的代码将不会进行类型检查,就好像Tyescript正在考虑mod.d.ts文件而不是mod.js文件一样。
为了在Deno中支持这一点,Deno有两个解决方案,其中有一个增强支持的解决方案的变体。您遇到的两种主要情况是:
- 作为 JavaScript 模块的导入器,我知道应该将哪些类型应用于该模块。
- 作为 JavaScript 模块的供应商,我知道应该将哪些类型应用于该模块。
后一种情况更好,这意味着您作为模块的提供者或宿主,每个人都可以使用它,而不必弄清楚如何解析JavaScript模块的类型,但是当使用您可能无法直接控制的模块时,还需要能够执行前一种操作。
导入时提供类型
如果您正在使用JavaScript模块,并且您已经创建了类型(.d.ts文件)或以其他方式获得了要使用的类型,则可以使用@Deno-Types编译器提示指示Deno在进行类型检查时使用该文件,而不是JavaScript文件。@Deno-Types需要是单行双斜杠注释,使用该注释时会影响下一条导入或重新导出语句。
例如,如果我有一个JavaScript模块CoolLib.js,并且我有一个想要使用的单独的CoolLib.d.ts文件,我会这样导入它:
// @deno-types="./coolLib.d.ts"
import * as coolLib from "./coolLib.js";
当输入检查CoolLib及其在文件中的使用情况时,将使用CoolLib.d.ts类型,而不是查看JavaScript文件。
编译器提示的模式匹配在某种程度上是宽容的,它将接受说明符的引号和非问号值,以及接受等号前后的空格。
托管时提供类型
如果您控制了模块的源代码,或者您控制了文件在Web服务器上的托管方式,则有两种方法可以通知Deno给定模块的类型,而不需要导入器执行任何特殊操作。
使用三斜杠参考指令
Deno支持使用三斜杠Reference指令,该指令采用TypeScript在TypeScript文件中使用的引用注释来包括其他文件,并将其应用于JavaScript文件。
例如,如果我已经创建了CoolLib.js,并且在CoolLib.d.ts中为我的库创建了类型定义,那么我可以在CoolLib.js文件中执行以下操作:
/// <reference path="./coolLib.d.ts" />
// ... the rest of the JavaScript ...
当Deno遇到此指令时,它将解析./CoolLib.d.ts文件,并在类型检查文件时使用该文件而不是JavaScript文件,但在运行程序时仍会加载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
要点
类型声明语义
类型声明文件(.d.ts文件)遵循与Deno中的其他文件相同的语义。这意味着假设声明文件是模块声明(UMD声明),而不是环境/全局声明。Deno将如何处理环境/全局声明是不可预测的。
此外,如果类型声明导入其他内容,如另一个.d.ts文件,则其解析遵循Deno的正常导入规则。对于许多在Web上生成并可用的.d.ts文件,它们可能与Deno不兼容。
为了克服这个问题,一些解决方案提供商,如Skypack CDN,将自动捆绑类型声明,就像它们提供作为ESM的JavaScript捆绑包一样。
Deno 友好的CDNs
有一些CDN托管与Deno很好集成的JavaScript模块。
- Skypack.dev是一个CDN,当您将?dts作为查询字符串附加到远程模块导入语句时,它提供类型声明(通过X-tyescript-Types头)。例如:
```
import React from "https://cdn.skypack.dev/react?dts";
```
类型检查时JavaScript的行为
如果在Deno中将JavaScript导入到TypeScript中,并且没有类型,即使您将checkJs设置为false(Deno的默认设置),TypeScript编译器仍将访问JavaScript模块并尝试对其进行一些静态分析,以至少尝试确定该模块的导出的形状,以验证TypeScript文件中的导入。
在尝试导入“常规”ES模块时,这通常不是问题,但在某些情况下,如果模块有特殊包装,或者是全局UMD模块,则TypeScript对模块的分析可能会失败并导致误导性错误。在这种情况下,最好的做法是使用上面提到的方法之一提供某种形式的类型。
Internals
虽然不需要了解Deno的内部工作方式就能很好地利用Deno中的TypeScript,但它可以帮助理解它是如何工作的。
在执行或编译任何代码之前,Deno通过解析根模块,然后检测其所有依赖项,然后递归地检索和解析这些模块,直到检索到所有依赖项,从而生成模块图。
对于每个依赖项,都会使用两个潜在的“槽”。这里有代码槽和类型槽。在填充模块图时,如果模块是或可以发送到JavaScript,它将填充代码槽,并且仅类型依赖项(如.d.ts文件)将填充类型槽。
构建模块图时,如果需要输入检查图形,Deno将启动TypeScript编译器,并将可能需要作为JavaScript发出的模块的名称提供给它。在此过程中,TypeScript编译器将请求额外的模块,Deno将查看依赖项的槽,如果它已填满,则在提供代码槽之前为其提供类型槽。
这意味着,当您导入.d.ts模块,或者使用上面的解决方案之一为JavaScript代码提供替代类型模块时,解析该模块时将提供给TypeScript。
网友评论