# 为什么 Babel 要支持编译 TypeScript

4,129 阅读5分钟

目前 TypeScript 的编译有两种方式。一种是使用 TypeScript 自家的编译器 typescript 编译(我们后面会用统一称为 TS 编译器),一种就是使用 Babel + @babel/preset-typescript 编译。所以,当我们使用 TypeScript 开发项目的时候,遇到的第一个问题就是,我们应该选择哪种编译方式?

文章中提到 TS 编译器,指代 TS 编译器自身以及在其之上封装的各类和打包工具配合的插件

TL; DR

  1. Babel 编译没有类型检查,部分语法不支持;
  2. Babel 编译一定要配合 TS 编译器一起使用;
  3. Babel 编译有其自身优势;
  4. Babel 与 TypeScript 结合是最好的选择;

做出选择之前,我们需要了解下它们之间的区别。

Babel 编译的劣势

TS 编译器就不说了,它就是对 TypeScript 语言定制的。这里我们先说一下 Babel 编译。

TypeScript 团队发布的文章里说明了如何使用 Babel 编译 TypeScript 。同时也提到了,Babel 编译 TypeScript 是不会做类型检查的,或者说,所有的类型声明都会被 Babel 抛弃掉[1];另外,Babel 是不支持编译部分语法的[2]

部分语法不支持基本可以忽略,因为主要是一些 TypeScript 不推荐的旧的语法。但是没有类型检查就是很棘手了。即使在开发过程中,编辑器会提示我们类型错误,但是在编译前,还是要执行一次统一的类型检查,就像我们做 eslint 检查一样。所以这个时候你会发现,如果你要使用 Babel 编译,那么你必须要配合 TS 编译器一起使用。因为 TS 编译器是会有类型检查的。

可是明明使用 TS 编译器就可以完整的编译 TypeScript,那为什么还要多此一举的使用 Babel 编译呢?

Babe 编译的优势

这个时候就不得不提 Babel 编译的优势了。

灵活性

Babel 能够根据目标环境转译指定语法。这一点 TS 编译器是不支持的。TS 编译器只能指定将 TypeScript 编译为 es3 es5 es6 或类似的某个 ECMAScript 版本。但是在 Babel 里通过 @babel/preset-env 你可以指定目标环境,比如 "targets": { "ie": "11" },那么 Babel 只会编译 ie11 不支持的语法,所有 ie11 支持的语法 Babel 都不会做转译。在编译的灵活性上,只要你想,你可以控制 Babel 只编译你需要编译的语法。

polyfill

Babel 能够根据目标环境自动添加 polyfill。这一点 TS 编译器是不支持的。如果是新语法的转译,TS 编译器会添加一些辅助函数。但是任何需要修改运行时环境的 polyfill,TS 编译器是不会帮你添加的。比如你的代码中用到了 Promise Set Map 或者 Object.assign Array.includes,那么你需要自己手动添加相应的 polyfill。但是在 Babel 里通过@babel/preset-env ,Babel 会判断目标环境,然后自动帮你添加所有你需要用到的 polyfill。

插件系统

Babel 有插件机制,并且有着活跃的插件生态。这一点 TS 编译器是不支持的。TS 编译器没有插件系统,并不允许你添加自己的插件。但是在目前的开发中,我们都或多多少的受益于 Babel 各种各样的插件,而且很多人也都写过自己的 Babel 插件,去自定义转译规则。

以上就是 Babel 编译的优势。或许可以作为为什么 Babel 要支持编译 TypeScript 的原因。如果你还了解其他 Babel 编译的优势,记得在评论区留言呀。

Babel 支持编译 TypeScript 才是最好的选择。

如果你去翻阅 TypeScript issues 的话,你会发现很早之前,就有人给 TypeScript 提建议,希望他们能够支持自动的引入 polyfill[3],希望他们能够支持插件机制[4]。但是官方的回复都是这些并不在他们的考虑范围内。

另外,如果你去查阅 Babel PR 的话,你会发现,给 Babel 提交代码支持 TypeScript 的人是 TypeScript 团队的人[5]

所以,其实不是 Babel 要主动去支持 TypeScript,而是 TypeScript 团队需要得到 Babel 的支持,希望 Babel 能够弥补 TS 编译器的“缺陷”。

另一方面,TypeScript 没有选择自己去实现自动引入 polyfill,去实现插件系统,其实对大家都是有好处的。因为,TypeScript 没有必要把 Babel 的能力再去实现一遍,而且如果 TypeScript 要去添加自己的插件系统,那么原本在 Babel 生态下的各种各样的插件,在 TypeScript 生态下还要再来一次。

因此,TypeScript 选择让 Babel 去支持编译 TypeScript 才是最好的选择。而且,当 TypeScript 和 Babel 结合的时候,或许才是最完美的。

总结

pros-and-cons

TS 编译和 Babel 编译的优缺点如上。无论如何,TS 编译器都是必须的。即使使用 Babel 编译,也应该配合 TS 编译器一起使用,利用 TS 编译器做类型检查,然后使用 Babel 完成转译工作。至于你是否应该使用 Babel 编译,要看你是否需要 Babel 编译的优势,是否依赖 Babel 生态下的各种插件。

最后附上一个 TypeScript 的 demo 项目 ts-hello。在项目里你可以选择使用 TS 编译器编译项目还是 Babel 编译项目。你也可以看到如果使用 Babel 编译,应该如何跟 TS 编译器一起配合使用。

最后留一个小问题:既然 Babel 都支持 TS 编译了,为什么不支持类型检查呢?是做不到吗?