Editor’s note: This post was last updated on 10 July 2023 to be accurate with the updates in Babel 7.21.0.
Babel 7 was released in 2018 and since then, updates have allowed developers to use Babel and TypeScript without ever needing to complicate their builds with the TypeScript compiler. But what are the differences between using Babel and the TypeScript compiler? And should you use Babel or TypeScript for your next project? This article aims to answer these questions and more.
We’ll cover the following:
Here are the new updates since the latest feature at the time of writing, v7.21.0:
PrivateFieldsAsSymbols assumption for Classes: This update introduces the
privateFieldAsSymbols assumption for classes, allowing private fields in classes to be treated as symbols. Private fields are declared using the
# symbols before the field name
regexp modifiers proposal, which allows regular expressions to have custom flags or modifiers
const modifier in type parameters: TypeScript support in Babel now includes support for the
const modifier in the type parameter. The
const modifier can be used to specify that a type parameter should be treated as immutable, providing more expressive and robust typing capabilities in TypeScript
new.target outside functions: Babel introduced a parser option that allows the usage of
new.target outside functions.
annex: false, to disable Annex B behavior. Annex B is a set of optional backward compatibility features in the ECMAScript specification, and this option allows developers to opt out of these behaviors, providing more control over the language features
.cts as configuration file: Babel now supports the
.cts file extension as a configuration file. Having more configuration file options allows developers to organize and manage their Babel configurations more efficiently, using the file extension that suits their project needs
export type* from in TypeScript: Babel now supports the
export type * from syntax in TypeScript. This syntax allows re-exporting all named exports from one module to another, providing better modularity and reusability in TypeScript projects
The TypeScript compiler,
Here’s an overview of the compilation process:
.ts) as input. These files contain TypeScript-specific syntax, including static typing annotations, interfaces, classes, and other TypeScript language features
.d.ts) as output. These files are now ready to be executed by browsers or Node.js
Here’s how Babel contributes to the integration of TypeScript:
The transpilation process ensures that the TypeScript features are transformed into syntax and constructs compatible with the target environment.
Developers can leverage Babel plugins to fine-tune the transpilation process, making it compatible with the specific needs and requirements of the project.
Babel is commonly used in React.js development to support JSX syntax, which is not natively understood by browsers. JSX allows developers to write React components more intuitively.
When using TypeScript with React, Babel ensures that the JSX syntax in TypeScript files is properly transpiled, allowing developers to write JSX components in a TypeScript environment.
Babel’s extensive plugin ecosystem allows developers to add custom plugins and presets that cater to specific TypeScript use cases. Developers can choose from a range of plugins to support experimental TypeScript proposals, implement custom transformations, and fine-tune the transpilation process.
Babel often supports TypeScript features that are still in the proposal stage or not yet standardized by ECMAScript. This allows developers to experiment with new TypeScript features before they are officially adopted, giving them early access to the latest language capabilities.
Babel provides an automatic polyfilling mechanism through the
@babel/preset-env preset. When you configure Babel with
@babel/preset-env, it automatically detects the ECMAScript features used in your code and includes the necessary polyfills only for the features that are missing in the target environment.
For example, if you use a modern ES6 feature like
Promise in your code and the target environment lacks support for it, Babel’s auto polyfill will include a minimal and optimized polyfill for
Promise in the output code. This approach ensures that your code runs correctly in older browsers that lack native
The advantage of auto polyfilling is that it reduces the size of the output bundle by only including necessary polyfills, making the application more efficient.
TypeScript itself does not provide built-in polyfilling like Babel. TypeScript focuses on static type checking and transpilation, leaving polyfilling to be handled separately. TypeScript users typically use Babel as part of their build process, and Babel takes care of both transpilation and polyfilling using the
When TypeScript code is transpiled by Babel, the auto polyfilling mechanism comes into play, ensuring that any missing features required by the TypeScript code are properly polyfilled. Additionally, TypeScript developers often use other tools, like
@babel/polyfill, directly to include specific polyfills based on the project’s needs and the targeted environments.
To include explicit polyfills in a TypeScript project, you can import the necessary polyfills at the entry point of their application. For example:
import 'core-js'; // Import all core-js polyfills // or import 'core-js/features/promise'; // Import only the Promise polyfill
core-js or other polyfill libraries gives developers more control over which polyfills are included and allows them to customize the polyfilling behavior.
There are some major differences between using TypeScript and using TypeScript with Babel. We’ll look at the five most important differences.
Babel doesn’t care about your fancy TypeScript types. It just throws them in the trash, without checking that they’re right. The example below compiles without any errors or warnings with Babel, but not with TypeScript:
const myCoolString : string = 9;
9 is definitely not a string Babel. Removing the types can be excellent for quick prototyping where you want the code to compile, even if your types aren’t on point.
If you’re going through the effort of typing things, at some point you’ll probably want to check that they’re right. Luckily, this isn’t a big deal. You can either let your editor take care of it or run
tsc --noEmit, which typechecks your project without compiling anything.
By default, TypeScript compiles an entire project at once, while Babel only compiles one file at a time. Previously, this meant that Babel did not support TypeScript features that required reading multiple files — such as
However, this is no longer true since Babel 7.15, which was released in 2021. This means that if you’re on the newest version of Babel, you should be able to compile all valid TypeScript codebases.
TypeScript was a bit early to the decorator party (if you’re unsure what a decorator is, this is a good introduction to decorators). After TypeScript implemented decorators, the decorator proposal has changed multiple times and is still not finalized.
What this means is that, currently, the ECMAScript spec and TypeScript don’t quite see eye-to-eye on how decorators should behave. Babel’s plugins follow the ECMAScript spec, which means that Babel doesn’t compile decorators the same way that TypeScript does. Luckily for us, Babel has a
legacy mode to compile decorators with the old behavior. Simply add the Babel plugin
"@babel/plugin-proposal-decorators" with the
legacy option set to
There’s one other TypeScript decorators feature we should talk about:
emitDecoratorMetadata. TypeScript normally erases all type information so it doesn’t exist at runtime.
emitDecoratorMetadata is a feature that keeps the types around for classes and methods that have a decorator applied to them.
Having the type at runtime allows us to do all sorts of fancy things, such as dependency injection and mapping the TypeScript types to types in an SQL database. The feature sees reasonably heavy use in those two areas, with libraries such as TypeORM, TypeGoose, inversifyJS, and even Angular’s dependency injection system depending on this feature.
As Babel doesn’t care about your type information, this feature requires a custom plugin, babel-plugin-transform-typescript-metadata. Adding that plugin along with
plugin-proposal-decorators, which we mentioned earlier, should give Babel feature parity with TypeScript in regards to decorators.
Babel is much more extensible than TypeScript. There are plenty of plugins that optimize your code and help you strip out unused imports, inlines, constants, and much more. While TypeScript has its own Transformer API, which allows for custom transformations, the Babel ecosystem is both richer in plugin choices and much more accessible.
If you need custom transformations, you’ll need to use Babel. The good news is that most TypeScript tools allow you to use TypeScript and then run the code through Babel afterwards, to get the best of both worlds. But this obviously comes with additional complexity in your build-chain.
Comparing Babel and TypeScript in regards to performance is difficult and probably won’t give you the full picture. TypeScript that performs type-checking will definitely be slower than Babel because there are extra steps included there.
To reach approximately equal speed, you can mitigate that slowdown by using something like fork-ts-checker-webpack-plugin, which runs the compilation without types in one process and the type-checking in a background process.
At this point, TypeScript and Babel are approximately equal in the role they can play in your build chain. Babel now has full support for
const enums, decorators, and decorator metadata. The only downside to using Babel is that you will need to run your type-checking as a separate process.
Do you have any experiences with TypeScript and Babel? I’d love to hear about them. @GeeWengel
LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
Discover the most popular libraries for enabling Bluetooth Low Energy (BLE) in React Native apps, allowing them to interact with Bluetooth hardware on iOS and Android platforms.
CRDTs, or conflict-free replicated data types, is a concept that underlies applications facing the issue of data replication across a […]
We explore the fusion of TensorFlow and Rust, delving into how we can integrate these two technologies to build and train a neural network.