Using new Babel 7 and preset-typescript to compile Angular 6 app

Recent launch of Babel 7 was accompanied by a pretty large fanfare, mostly due to long time since last release. Previous version was released almost two years ago — a plenty of time to fill it with various optimizations and new features to toy with.

One of the most anticipated traits of the new version is a plugin for TypeScript. That’s right — this means we can compile TS to JavaScript without necessity of using any external TypeScript loaders nor tsc itself. There is a significant number of caveats, but still — it’s quite a feat and a promising approach for the future.

Usage of this new approach is described as pretty straightforward: just babel-process the file using proper preset and voila — our code is transpiled. Let’s try to do it ourselves — and even try to build an Angular app with that.

Basic example of @babel/preset-typescript usage

Before we take a deep dive, let’s see if it works in its basic form. As an example, we’ll try to compile following TypeScript code file:

Nothing too complicated here — we have an interface describing a vehicle, then specific Car class. At the end, we create an instance and invoke some simple logic inside.

We’ll save this file as testInput.ts and put into empty folder. Then, we need to initialize npm and install required packages:

npm init
npm install @babel/core @babel/preset-typescript @babel/preset-env

As soon as we have babel CLI (@babel/cli) installed, we’re good to go. Remember that all @babel packages should be in at least 7.x version — it’s the earliest that support TypeScript! Now, let’s invoke our transpilation:

babel testInput.ts --out-file testOutput.js --presets @babel/preset-typescript

Voila! As long as no errors are produced, we should have an JavaScript output generated totestOutput.js file. Let’s check what’s inside:

Well, it’s… pretty much the same code, but with all type-related information stripped away. No types, no interfaces — just like someone have just plucked them out of the code. But after all, it was our main goal — we have successfully compiled valid TypeScript code without tsc or any specialized Webpack loader.

As we can see, the class syntax is still here. If we want to target our code to ECMAScript 5 standard, then we need to add another preset to our pipeline:

babel testInput.ts --out-file testOutput.js --presets @babel/preset-env,@babel/preset-typescript

Now, we should expect proper ES5 code on the output — with all required shims and transforms:

Okay, so we know that it works. Next, instead of invoking babel by CLI, we could move it to Webpack configuration — then by using babel-loader, put everything into nicely working pipeline. Still, it doesn’t look like a revolution — after all, we could just wire up TypeScript loader just after babel loader. So, what’s the point? There is one small thing achieved here: we have simplified the build process and removed one large block, which is external TypeScript compiler. It’s a beginning of something now — and surely a promising step in the process of streamlining our toolchain.

Let’s get to the downsides: there is a HUGE caveat of this approach. Namely, TypeScript gets compiled but… doesn’t get type-checked. “What!?”, you can ask — and it would be pretty valid resentment.

It’s true — Babel’s preset is capable of transforming TypeScript code — but does not check type validity. You can try it out yourself —if you’ll break the code above and cause any explicit type error (e.g. assigning a number to a string), babel would let you get away with it. So again, is it worth it? Well, it depends — after all, you can rely on your IDE for displaying type errors and afterwards invoke external type-checking on a commit hook. Does it make sense? I don’t know. But keep in mind that after all, those are just baby steps of this tool.

If we have TypeScript, why not Angular?

Okay, but we’re not doing it just to toy around with transpiling single class— we’d certainly want to see preset-typescript in action with something serious. Angular, our beloved framework is a first obvious candidate — TypeScript language is its natural environment and currently the only recommended approach to writing applications.

Currently, there are few problems related with pipelines that build Angular-based applications. First, we have Angular CLI that gives us everything — but since Angular 6, it’s not possible to eject and change its config. It can put us in a dead end if we need to seriously modify the pipeline.

On the other side, it was always possible to build our own webpack-based stack for Angular without using its CLI. This way, we had everything under strict control and could modify the building process in any desired way. It have its downsides: we need to wire up and maintain all loaders, which usually involves mixing native TypeScript loaders and babel. Our code had to go through many different software packages and elements of the stack, which resulted in reduced performance and compatibility issues.

Babel 7 with its preset-typescript looks like a remedy here . Let’s give it a shot and try to create smallest possible, webpack-based Angular config ever — using new Babel!

First, let’s define our project structure. It will be dead simple:

project directory
⌞ dist
⌞ index.html
⌞ output.js
⌞ src
⌞ app
⌞ app.component.ts
⌞ app.module.ts
⌞ index.ts
⌞ .babelrc
⌞ package.json
⌞ webpack.config.js

Single module, single component and an entry file named index.ts in source directory. Apart from that, we’ll have dist folder for generated output and configuration files for webpack, npm and babel.

Let’s install packages first. We’re going to need:

# Babel-related packages
npm install @babel/core @babel/preset-typescript @babel/preset-env @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
# Webpack-related packages
npm install webpack webpack-dev-server babel-loader
# Angular packages
npm install @angular/core @angular/common @angular/compiler @angular/platform-browser-dynamic
# Polyfills for angular
npm install core-js zone.js

That’s quite a lot, but in the end we’re still going to end with simpler config than utilizing official TypeScript tools.

Next, let’s produce our webpack configuration file:

Again, it’s dead simple. We instruct webpack to put*.ts files through babel — and that’s everything related to processing itself. Apart from that, we set up supported extensions and indicate input and output files.

Let’s move to .babelrc file:

This one is a little more tricky. As before with CLI, we have instructed babel to load preset-env and preset-typescript, but that’s not everything. Since Angular utilizes decorators and class properties heavily and Babel doesn’t support them in those presets, we need to add them manually. Please note that plugin-proposal-decorators needs to be additionally configured — we need to use legacy decorators mode. I’ve experimented with various plugins here —apparently @babel/plugin-syntax-decorators doesn’t do the work and causes transform errors.

Okay, now — let’s write some code. Let’s fill our TypeScript files with following Angular code:

We can run the app either by adding proper entry to package.json “scripts” section, or by invoking webpack by npx:

npx webpack-dev-server --mode development --content-base=./dist/

Now, if no errors occured, open your browser and navigate to http://localhost:8080. You should see your Angular app up and running:

Similarly, you can invokenpx webpack --mode production and see how minified bundle pops out in dist folder.

Ladies and gentleman, we have just constructed world’s simplest Angular-building toolset that doesn’t employ pesky Angular CLI.

Sounds proud, but… we’ll still toying around. Keep in mind that we still don’t have explicit type checking on board — which doesn’t put this approach in any serious position.


Wrapping up

Still, it’s very good to see that our tools are getting simpler. The less tools we need to employ, the faster and cleaner builds we will get. Also, it impacts our learning curve — it’s certainly easier when you don’t need to know details about ten of various unrelated tools.

I’m strongly rooting for next releases of babel and its presets. Hopefully, we will soon have smooth and complete solution for building various apps in TypeScript and other languages only by using one tool.

If you want to play around with preset-typescript yourself, don’t hestitate to clone my repository — let me know if you’ll discover something more:

https://github.com/hzub/angular-6-with-babel-typescript

Cheers!

Like what you read? Give Hubert Zub a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.