DEV Community

Cover image for Is babel still relevant for TypeScript projects ?
Manuel Beaudru
Manuel Beaudru

Posted on

Is babel still relevant for TypeScript projects ?

Since TypeScript has the ability to generate a JavaScript bundle using the TypeScript compiler, are there still reasons to bother using babel and webpack when you want to create a new ts-based project ?

Unsatisfying answers

My 5min google search on the topic didn't give me satisfying answers:

  • on reddit, there are many voices arguing that the tsc is enough (1, 2)
  • but most project scaffolding tools (create-react-app, nextjs, tsdx to name a few) still rely on babel + webpack or rollup.
  • Besides, TypeScript folks themselves state that "TypeScript code is transformed into JavaScript code via the TypeScript compiler or Babel" (1), or that "You might use [babel] for speed or consistency with your existing build tooling"(2).

Why is that ? Should you solely rely on tsc at your company or on your next pet project ?

I've done the work for you, so let's dive in 🙂

👉 Want to see more ? Follow me on Twitter

TypeScript is a self-sufficient build tool

The TypeScript compiler has the ability to produce a JavaScript bundle. It is capable of generating an "older" JS for specific targets like ES5 or specific nodeJS versions.

For instance, I created the most minimalistic TypeScript project without configuring any options, and this is what I got:

Input (index.ts):

const helloWorld = () => {
  console.log("hello world");
};
Enter fullscreen mode Exit fullscreen mode

Output (index.js):

"use strict";

var helloWorld = function () {
  console.log("hello world");
};
Enter fullscreen mode Exit fullscreen mode

TypeScript has generated a default tsconfig.json that targets ES5, so the arrow function has been transpiled to a regular function and the const has been changed for a var.

As you can see, tsc has done a pretty good job at transpiling the code. Indeed, it is a legit transpiler and it might be enough for you. Besides, we didn't needed to get webpack into the mix and it was very easy to set up.

At this point you might be wondering, what does the TypeScript compiler is missing that Babel isn't ?

Babel comes with many benefits

If you look at the surface, indeed Babel and TypeScript are both capable of transpilation, but babel is capable of covering a much broader range of scenarios and the power to fine-tune what code is generated.

Optimized build for legacy browsers and modern browsers 🚀

With Babel + babel-preset-env, you have more control on what your target is. For instance you can target every browser above IE11, or > 0.25% of browsers that are not dead.

Going further, you can even generate two bundles: one for legacy browsers, and one for modern browsers that will be much lighter & easy to process.

You can learn more about this topic on the two following articles, that I particularly enjoyed:

Tree-shaking and Quality of life improvements ⛱️

Let's dive in into the TSDX case:

TSDX is a zero-config CLI that helps you develop, test, and publish modern TypeScript packages

I find this example very interesting, because this tool is solely focused on generating TypeScript packages, but they still use babel anyway.

In a nutshell, they use babel to:

  • Generate an optimized production bundle, that will remove development specific instructions like this:
if (__DEV__) {
  console.log('foo');
}
Enter fullscreen mode Exit fullscreen mode
  • Prevent you from accidentally import all lodash by rewriting the imports at transpile time + changes lodash to lodash-es to make your library treeshakable to end consumers.

A rich and powerful plugin ecosystem 📦

For instance, styled-components provides a babel-plugin to add support for server-side rendering, minification of styles, and a nicer debugging experience..

In this particular case, there is an alternative to make it work without babel, but:

  • it's not as feature-rich as the official babel plugin (ref)
  • it requires to use webpack in combination of the ts compiler, since the tsc doesn't allow plugins to apply code transformers (ref)

Besides, this plugin is one of the most popular ones so there is a babel alternative. But you are not guaranteed to have the same escape-hatch for other transformers.

Retro-Compatibility 📼

For instance, create-react-app and NextJS still need to support non-TypeScript powered projects (ex: JavaScript or Flow), it makes sense for those tools not to center their entire system around the TypeScript compiler.

For the same reason, it's also easier in a legacy JavaScript codebase built with babel+webpack to migrate to TypeScript incrementally and with confidence, since it will only consist of adding the @babel/preset-typescript to the mix for .ts and .tsx files.

You won't need to "remove" babel from your pipeline and touch your existing configuration.

Build-time performance ⚡

When using babel to transpile your TS into JS, there is no type-checking that is performed on the TypeScript codebase: babel simply "removes" every TS-specific instructions and only keeps the JS, then applies its transformations.

This can arguably be seen as a downside, since TS code with invalid typing will be built without crashing or warning you of issues.

But you can see it as an upside: you can run tsc in a process just for the type-checking phase, and babel in a parallel process to generate the build.

🔥 Besides, there are babel alternatives growing up like SWC or Sucrase that are heavily focused on compilation speed and are much faster than the tsc transpiler or babel.

Conclusion

TypeScript compiler is capable of transpiling your code, but it is not as flexible, powerful and complete as Babel.

If you are starting a new project from scratch, you're probably better of using a generator like NextJS for a web app or TSDX to build a TS library. TS doc suggests the same thing, the Bootstrap page is very useful in that regard.

Such tools are making the choice and configuration of the transpiler behind the scenes, and most of them use Babel for all the reasons we saw together.

And if you're a company that wants to have all the controls on the tooling, my advice would be to use TypeScript compiler solely for the type-checking phase and Babel or SWC for the transpilation phase.

Indeed, babel gives you the fine-tuning control you might need, while SWC gives you the speed if you don't need what babel provides.

What do you use in your project/company ? Do you have a use case I didn't cover that you want to highlight ? Don't hesitate to react in the comments and I'll gladly respond 👍

Top comments (12)

Collapse
 
alexburner profile image
Alex Burner

One important aspect of babel-preset-env I think you missed: Browser Polyfills. TypeScript can transpile syntax down for older browsers, but it won't add runtime code to support something like String.prototype.replaceAll() in IE11

Collapse
 
mbeaudru profile image
Manuel Beaudru

Indeed, I will update the post this weekend to add this point 👍

I thought that TypeScript added polyfills, but it doesn't and adding them is not trivial either.

Thanks !

Collapse
 
jackmellis profile image
Jack

Great article! Babel is totally still useful, there are a lot of things babel can do over tsc, like literally anything. Until typescript has a plugin system where you can easily include extra transpilers and parsers, babel will always be the more flexible option. If anything I feel like since the typescript preset came out, the question should be "do we still need tsc?"

Collapse
 
mindplay profile image
Rasmus Schultz • Edited

Honestly, I view tsc as a "proof of concept" compiler at best - incapable of even linking an npm package, it has honestly never been of any real practical use on real projects in the real world.

It's useful for the language service and for type-checking with --noEmit and not much else.

It's kind of sad, really. The compiler itself is like a frustrating teaser - just enough to get someone trying out TS and get them interested, but not enough for them to actually do anything really useful at all.

It gets to the point of poignant, really - the fact that the Playground allows you to import from npm packages, but not actually run the code... I don't know whether to laugh or cry.

They ought to either retire it or at least add basic support for linking dependencies. 😕

Collapse
 
mbeaudru profile image
Manuel Beaudru

Thank you !

Pretty much my conclusion indeed, I don't see a scenario where using tsc for the transpiling phase is better now?

But it was not obvious at a first glance, as the reddit answers and my own interrogation shows. Maybe that's because as you said, at the beginning of TypeScript, Babel wasn't an easy option since the TS plugin didn't existed

Collapse
 
gnt profile image
gntsketches

By any chance could you link or provide a (minimal) example of how to configure "use TypeScript compiler solely for the type-checking phase and Babel or SWC for the transpilation phase"?

I'm experienced with JS but relatively new to working with all the above, and with compilers in general. I've studied the basics of Webpack, Typescript and Babel, but am still not very clear on how they all fit together.

Thanks for any time you can offer on this!

Collapse
 
gnt profile image
gntsketches

This seems to be a good example: learntypescript.dev/12/l2-babel

Looks like the relevant parts are:

  • set noEmit to true in tsconfig
  • use babel's preset-typescript to accomplish the TS complication
Collapse
 
ekeijl profile image
Edwin

I'm in the process of figuring out a best practise for this too.
The approach you linked makes sense, but I'm still not sure where in the process typechecking is done.

Can we completely rely on our IDE to handle this for us? Do we need to run tsc manually every now and then?

Thread Thread
 
gnt profile image
gntsketches

It's a good question. I think the type-checking must happen "first"... ie Babel runs the TS checks, transpiles it to JS, then transpiles JS to ES5 (or whatever the settings are.) Check out this post: stackoverflow.com/questions/383202...

A helpful comparison is to a setup without @babel/preset-typescript (ie: webpack applies TS, then Babel. (See this post: stackoverflow.com/questions/322343...)

In this webpack.config.js, if the order of the babel-loader and ts-loader rules are reversed, the app crashes:

module: {
rules: [
{
test: /\.(j|t)s$/,
exclude: /node_modules/,
use: {
// without additional settings, this will reference .babelrc
loader: 'babel-loader',
}
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: 'ts-loader'
}
},

Thread Thread
 
gnt profile image
gntsketches

Ah, also to your questions, with a webpack setup, tsc does not need to be run manually. It's also not needed to specify outDir or outFile in .tsconfig - that is all handled by Webpack.

Not so sure about the IDE portion of this, that is an added complication that often confuses me too. I've found it helpful to make a conceptual separation between code-processing (ie: webpack typescript babel), and looking-at-code (IDEs). Generally I think that once a language or plugin is installed in an IDE, it is supposed to look for the settings in your .config files for any give project to know what to highlight. (But don't quote me on that.)

Collapse
 
beyarz profile image
Beyar

Very interesting!

Collapse
 
svyatogor007 profile image
Sergey Kuleshov

Thanks, very useful. I was wondering the same question but never bothered to properly investigate. What I am wondering if it still makes sense to use babel in component libraries, which are meant to be included in other projects backed with Webpack for example, which will will run through babel anyways. Many solutions (e.g. nx) suggest that you do.