使用真正的、原生的、ECMAScript模块的工作正在成为一件事。Vite、ES Dev server和Snowpack等工具通过将模块解析留给浏览器来获得快速开发体验。像Skypack和UnPKG这样的包CDN提供了预编译的ES模块,你可以通过引用一个URL在Deno和浏览器中使用。
结合适当的缓存和对HTTP功能的了解,ES模块可以成为我们习惯的所有重型捆绑和构建的真正替代品。如果你想使用ECMAScript模块和TypeScript,有几件事需要考虑。
使用你自己的模块#工作
我们想要实现的是在TypeScript中编写import 和export 语句。
// File module.ts
export const obj = {
name: 'Stefan'
}
// File index.ts
import { obj } from './module'
console.log(obj.name)
但是保留语法,让浏览器处理模块的解析。要做到这一点,我们需要告诉TypeScript来
- 编译成能够理解模块的ECMAScript版本
- 使用ECMAScript的模块语法来生成模块代码
让我们在我们的tsconfig.json 中定义这个。
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
}
}
我通常使用esnext ,它总是最新的ECMAScript版本,但你可能想去一个特定的年份,这取决于你所使用的ES的其他功能。从es2015 开始的所有选项都是兼容的。
这已经做了一件重要的事情:它使语法保持不变。一旦我们想运行我们的代码,问题就出现了。通常情况下,我们从TypeScript文件导入时没有扩展名。指定一个ts 扩展名实际上会导致编译器错误。一旦我们进行编译,扩展名仍然缺失。但是浏览器需要一个扩展名来实际指向相应的JavaScript文件。
解决办法是。指定一个js 扩展名,即使你在开发时指向一个ts 文件。TypeScript很聪明,可以接收到这一点。
// index.ts
// This still loads types from 'module.ts', but keeps
// the reference intact once we compile.
import { obj } from './module.js'
console.log(obj.name)
tsx 文件也是如此。TypeScript知道tsx 文件会被编译成js 文件,所以一旦你导入,使用js 扩展名是安全的。
// Component.tsx
import { h } from 'preact';
export function Hello() {
return <div>
<h1>Hello World</h1>
</div>
}
// index.ts
import { Hello } from './Component.js';
console.log(Hello)
这就是你在本地所需要的一切!
通过HTTP工作的模块#
当我们想使用生活在特定URL下的依赖关系时,情况就会变得更有趣。比方说,我们想直接从Skypack或UnPKG导入*Preact*。
import { h } from 'https://cdn.skypack.dev/[email protected]^10.4.7';
TypeScript立即向我们抛出一个TS 2307错误。无法找到模块'...'或其相应的类型声明(2307)。TypeScript的模块解析是在文件在你的磁盘上,而不是通过HTTP在某个服务器上工作的。为了获得我们需要的信息,我们必须向TypeScript提供我们自己的解析。
有了类型#
比方说,我们想获得类型信息。我们可以指向TypeScript从我们的本地磁盘读取相应的类型信息。要么得到一个好的.d.ts 文件,要么通过NPM安装缺少的依赖性。
$ npm install [email protected]
或者根据你的库,只安装类型。
$ npm install @types/react
接下来,做一个路径别名,这样TypeScript就知道在哪里提取类型了。
{
"compilerOptions": {
...
"paths": {
"https://cdn.skypack.dev/preact@^10.4.7": [
"node_modules/preact/src/index.d.ts"
]
}
}
}
请确保你找到了正确的文件,否则,你的类型就会被搞乱。
没有类型#
TypeScript中最酷的事情之一是,我们可以决定哪些类型是我们想要的,哪些是我们不想拥有的。any ,这似乎是一种逃避,但它也可以是一种有意的选择,即在你的应用程序的某个部分不使用类型。
也许我们想加载一个模块,但我们并不真正需要了解接口,也不需要与API交互,为什么要费力地去连接类型呢?
TypeScript对导入有一个明确的任何,它被称为ts-ignore 。
//@ts-ignore
import { h } from 'https://cdn.skypack.dev/preact@^10.4.7';
// h is any
ts-ignore 将下一行从类型检查中移除。这也适用于我们应用程序的其他部分,不仅仅是导入。在我们的例子中, ,但TypeScript不知道类型,因为我们忽略了类型检查和推断。h
而在某些情况下,这完全没有问题。
Deno#
到目前为止,我们所听到的都是针对浏览器的,但还有一个运行时在TypeScript中使用ES导入。Deno。在Deno中,通过URL的ES导入是一流的公民,是导入依赖关系的首选方式。由于这与Deno的工作方式有很大关系,Deno对待导入的方式有点不同。
- 你写的所有东西都是TypeScript,所以不需要使用扩展程序
- 一旦你从URL导入,Deno就会向你抛出同样的TS 2307。但是当你第一次运行你的应用程序时,Deno会获取依赖关系,并可以从本地缓存的依赖关系中进行模块解析和类型提供。
- 如果你使用像Skypack这样的CDN,就有可能为常规的JS依赖性发送类型。请看Fred在Dev.to上的文章中如何操作。