阅读 535

[译] TypeScript 和 Babel:美丽的结合

Babel and TypeScript

由于 TypeScript 和 Babel 团队官方合作了一年的项目:TypeScript plugin for Babel@babel/preset-typescript),TypeScript 的使用变得比以往任何时候都容易。这篇文章会告诉你为何 TypeScript 和 Babel 是完美配对的 4 点原因,并会教你在 10 分钟内一步步地升级到 TypeScript。

哈?什么?为什么?

我一开始并不理解这个 preset 的必要性。

Babel 和 TypeScript 不是两个完全不一样的东西么?Babel 能怎么处理 TypeScript 的类型检查?TypeScript 早已能像 Babel 一样输出 ES5 代码,这有什么意义呢?把 Babel 和 TypeScript 合并起来不是会把事情复杂化么?

在几个小时的调研后,我的结论是:

TypeScript 和 Babel 是美丽的结合。

让我来告诉你原因。

1)你早已使用 Babel(或者应该如此)

你属于这三个类别之一:

  1. 你早已使用 Babel。如果不是直接使用,你的 Webpack 配置会把 *.js 文件提供给 Babel(大多数脚手架都是这种情况,包括 create-react-app
  2. 使用了 TypeScript,却没有用 Babel。针对这种情况,你可以考虑把 Babel 加入你的武器库中,因为它提供了许多独特的功能。继续阅读,你会了解更多!
  3. 你不使用 Babel?是时候开始用了。

编写现代 JavaScript 而不破坏任何东西

你的 JavaScript 代码需要在旧浏览器中运行?没问题,Babel 会转换代码,而且不会出现任何问题。使用最新和最好的功能,无需担心。

TypeScript 编译器具有类似的功能,可通过将 target 设置为 ES5ES6 来实现。但 Babel 配置通过 babel-preset-env 改进了这方面功能。你可以列出需要支持的环境,而不是锁定一组特定的 JavaScript 功能(ES5,ES6 等):

"targets": {
  "browsers": ["last 2 versions", "safari >= 7"],
  "node": "6.10"
}
复制代码

Babel 使用 compat-table 来检查要转换的 JavaScript 功能以及针对这些特定目标环境做 polyfill。

compat-table

花点时间欣赏那个将这个项目命名为 ‘compat-table’ 的天才。

create-react-app 使用了一种有趣的技术:在开发期间以最新的浏览器进行编译(为了速度),并在生产中以更大范围的浏览器进行编译(为了兼容性)。漂亮。

Babel 是超级可配置的

想要 JSX? Flow?TypeScript?只需安装一个插件,Babel 就可以处理它。有大量的 官方插件,主要涵盖即将推出的 JavaScript 语法。 还有很多第三方插件:improve lodash importsenhance console.log,或 strip console.log。在 awesome-babel 列表中找到更多信息。

不过要小心。如果插件显著改变了语法,那么 TypeScript 可能无法解析它。例如,备受期待的 optional chaining proposal 提议有一个 Babel 插件:

Optional chaining

@babel/plugin-proposal-optional-chaining

但不幸的是,TypeScript 无法理解这种更新的语法。

不要紧张,有另一种选择...

Babel 宏

你知道 Kent C Dodds 吗?他创造了一个改变游戏规则的 Babel 插件:babel-plugin-macros

你可以将宏安装为依赖项并将其导入代码中,而不是将插件添加到 Babel 配置文件中。当 Babel 正在编译时,宏会启动,并根据需要修改代码。

来看一个例子。 它使用 idx.macro 来解决痛点,直到 optional chaining proposal 提议通过:

import idx from 'idx.macro';

const friends = idx(
  props,
  _ => _.user.friends[0].friends
);
复制代码

编译为:

const friends =
  props.user == null ? props.user :
  props.user.friends == null ? props.user.friends :
  props.user.friends[0] == null ? props.user.friends[0] :
  props.user.friends[0].friends
复制代码

宏是相当新的,但很快就越来越受欢迎。特别是集成在 create-react-app v2.0 后。 CSS in JS 被覆盖:styled-jsxstyled-componentsemotion。Webpack 插件在被移植中:raw-loaderurl-loaderfilesize-loader。还有更多列在 awesome-babel-macros

这是最好的部分:与 Babel 插件不同,所有 Babel 宏都与 TypeScript 兼容。它们还可以帮助减少运行时依赖,避免一些客户端计算,并在构建时提前捕获错误。 查看 此帖子 了解更多详情。

Improved console.log

更好的 console.log:scope.macro

2)管理一个编译器更容易

TypeScript 需要它自己的编译器 — 它提供了惊人的类型检查超能力。

令人沮丧的日子(在 Babel 7 之前)

将两个独立的编译器(TypeScript 和 Babel)链接在一起并非易事。编译流程变为:TS > TS Compiler > JS > Babel > JS (again)

Webpack 经常用于解决这个问题。调整 Webpack 配置以将 *.ts 提供给 TypeScript,然后将结果提供给 Babel。但是你使用哪种 TypeScript loader?两个流行的选择是 ts-loaderawesome-typescript-loaderawesome-typescript-loaderREADME.md 提到它对一些工作负载来说可能更慢,并建议使用 ts-loader 加上 HappyPackthread-loaderts-loaderREADME.md 推荐结合 fork-ts-checker-webpack-pluginHappyPackthread-loader,和(或)cache-loader

啊。不。这是大多数人不堪重负的地方,并把 TypeScript 放在“太难”的篮子里。我不怪他们。

One does not simply configure TypeScript

阳光灿烂的日子(有了 Babel 7)

拥有一个 JavaScript 编译器不是很好吗?无论你的代码是否具有 ES2015、JSX、TypeScript 或疯狂的自定义功能 - 编译器都知道该怎么做。

我只是描述了 Babel。厚脸皮了。

通过允许 Babel 充当单个编译器,不需要使用一些复杂的 Webpack 魔术来管理,配置或合并两个编译器。

它还简化了整个 JavaScript 生态系统。他们只需要支持 Babel,而不是支持不同编译器的语法检查、测试运行器、构建系统和脚手架。然后,配置 Babel 以满足你的特定需求。告别 ts-nodets-jestts-karmacreate-react-app-typescript 等,并使用 Babel 支持代替。对 Babel 的支持无处不在,请查看 Babel 设置 页面:

Babel and TypeScript

Babel 覆盖你的需求。

3)编译速度更快

警告!有一个震惊的消息,你可能想坐下来好好听下。

Babel 如何处理 TypeScript 代码?它删除它

是的,它删除了所有 TypeScript,将其转换为“常规的” JavaScript,并继续以它自己的方式愉快处理。

这听起来很荒谬,但这种方法有两个很大的优势。

第一个优势:️⚡️闪电般快速⚡️。

大多数 Typescript 开发人员在开发/监视模式下经历过编译时间长的问题。你正在编写代码,保存一个文件,然后...它来了...再然后...最后,你看到了你的变更。哎呀,错了一个字,修复,保存,然后...啊。它只是慢得令人烦恼并打消你的势头。

很难去指责 TypeScript 编译器,它在做很多工作。它在扫描那些包括 node_modules 在内的类型定义文件(*.d.ts),并确保你的代码正确使用。这就是为什么许多人将 Typescript 类型检查分到一个单独的进程。然而,Babel + TypeScript 组合仍然提供更快的编译,这要归功于 Babel 的高级缓存和单文件发射架构。

因此,如果 Babel 剥离掉 TypeScript 代码,那么编写 TypeScript 有什么意义呢?这带来了第二个优势...

4)只有在准备好后才检查类型错误

你为了快速做出一个解决方案来看看你的想法是否有根据,会把一些代码改到一起。当你按下保存按钮的时候,TypeScript 向你尖叫:

“不!我不会编译这个!你的代码在 42 个不同的文件中有问题!”

是的,你知道它已经不能运行了。你可能也破坏了一些单元测试。但是你只是在这一点上进行实验。要在所有时间持续确保所有代码是类型安全的,这一点让人火大。

这是 Babel 在编译期间剥离 TypeScript 代码的第二个优点。你编写代码,保存,并且编译(非常快)而不检查类型安全性。继续尝试解决方案,直到你准备好检查代码是否有错误。这种工作流程可让你在编码时保持专注。

那么如何检查类型错误?添加一个调用 TypeScript 编译器的 npm run check-types 脚本。我将我的 npm test 命令调整为首先检查类型,然后继续运行单元测试。

这不是完美的结合

根据 公告文章,有四种 TypeScript 功能由于其单文件发射架构而无法在 Babel 中编译。

别担心,它没有那么糟糕。当启用 isolatedModules 标志时,TypeScript 将对这些问题发出警告。

1)命名空间。

解决方案:不要使用它们!他们已经过时了。请改用行业标准 ES6 模块(import / export)。推荐 tslint 规则 确保命名空间被使用。

2)使用<newtype>x 语法转换类型。

解决方案:使用 x as newtype

3)Const 枚举

这很羞愧。现在需要使用常规枚举。

4)传统风格的 import / export 语法。

示例:import foo = require(...)export = foo

在我多年 TypeScript 的使用中,我从未遇到过这种情况。谁这样编码?停下来!

好的,我准备尝试使用 Babel 和 TypeScript!

Yeah!

rawpixel.com 拍摄

我们开工吧!它应该只需要大约 10 分钟。

我假设你设置了 Babel 7。如果没有,请参阅 Babel 迁移指南

1)将 .js 文件重命名为 .ts

假设你的文件位于 /src 中:

find src -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.ts"' {} ;
复制代码

2)将 TypeScript 添加到 Babel。

安装一些依赖:

npm install --save-dev @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread
复制代码

在你的 Babel 配置文件里(.babelrcbabel.config.js)添加:

{
  "presets": [
    "@babel/typescript"
  ],
  "plugins": [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread"
  ]
}
复制代码

TypeScript 有一些 Babel 需要了解的额外功能(通过上面列出的两个插件)。

Babel 默认查找 .js 文件,遗憾的是,这在 Babel 配置文件中是不可配置的。

如果你使用 Babel CLI,添加 --extensions '.ts'

如果你使用 Webpack,添加 'ts'resolve.extensions 数组。

3)添加 “check-types” 命令。

package.json 里添加:

"scripts": {
  "check-types": "tsc"
}
复制代码

这个命令只是简单地调用 TypeScript 编译器(tsc)。

tsc 来自哪里?我们需要安装 TypeScript:

npm install --save-dev typescript
复制代码

为了配置 TypeScript(和 tsc),我们需要在根目录下有 tsconfig.json 文件:

{
  "compilerOptions": {
    // Target latest version of ECMAScript.
    "target": "esnext",
    // Search under node_modules for non-relative imports.
    "moduleResolution": "node",
    // Process & infer types from .js files.
    "allowJs": true,
    // Don't emit; allow Babel to transform files.
    "noEmit": true,
    // Enable strictest settings like strictNullChecks & noImplicitAny.
    "strict": true,
    // Disallow features that require cross-file information for emit.
    "isolatedModules": true,
    // Import non-ES modules as default imports.
    "esModuleInterop": true
  },
  "include": [
    "src"
  ]
}
复制代码

完成。

好了,设置完成了。现在运行 npm run check-types(监听模式:npm run check-types -- --watch)并确保 TypeScript 对你的代码满意。你可能会发现一些你不知道但却存在的错误。这是件好事!这份 从 Javascript 迁移 指南也会给你一些帮助。

Microsoft 的 TypeScript-Babel-Starter 指南包含其他设置说明,包括从头开始安装 Babel,生成类型定义(d.ts)文件,以及将其与 React 一起使用。

关于语法检查呢?

使用 tslint

更新(2019 年 2 月):使用 ESlint!自1月份以来,TypeScript 团队一直 专注于 ESLint 集成。由于 @typesript-eslint 项目,很容易配置 ESLint。如需参考,请查看我的 超级 ESLint 配置,其中包括 TypeScript、Airbnb、Prettier 和 React。

Babel + TypeScript = 美丽的结合

Love hearts

Akshar Dave 拍摄

Babel 是你需要的唯一一个 JavaScript 编译器。它可以被配置去处理任何事情。

没有必要与两个相互竞争的 JavaScript 编译器斗争。简化你的项目配置,并充分利用 Babel 与语法检查、测试运行器、构建系统和脚手架的惊人集成。

Babel 和 TypeScript 组合可以快速编译,并允许你专注地编码,只有在你准备好时才检查类型。

预测:TypeScript 使用将会上升

根据最新的 Stack Overflow 开发者调查,JavaScript 是最流行的语言,TypeScript 排在第 12 位。 对于TypeScript 来说,这仍然是一项伟大的成就,击败了 Ruby、Swift 和 Go。

Developer survey results

我预测 TypeScript 将在明年进入前 10 名。

TypeScript 团队正在努力推广。这个 Babel preset 是为期一年的合作,他们的新焦点是在 改进 ESLint 集成。这是一个聪明的举措 — 利用现有工具的功能、社区和插件。开发有竞争力的编译器和语法检查是浪费精力。

通往 TypeScript 的路径已经被铺平了,我们只需调整我们喜爱的工具配置即可。进入的障碍已被打破。

随着 VS Code 的普及,开发人员已经设置了一个很棒的 TypeScript 环境。写代码时的自动补全将带来欢乐的泪水。

它现在也集成到 create-react-app v2.0 中,将 TypeScript 展现给每月有 20 万次下载的用户。

如果你因为设置太难而推迟使用 TypeScript,这不再是一个借口。是时候试一试了。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

关注下面的标签,发现更多相似文章
评论