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.
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
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 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
testOutput.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:
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
This one is a little more tricky. As before with CLI, we have instructed babel to load
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 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 invoke
npx webpack --mode production and see how minified bundle pops out in
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.
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: