Short answer
You can’t, that’s not a thing (at least so far).
Longer answer:
By building an overly complicated front-end tool chain! Seriously, it’s crazy how much this is not out-of-box. Preface, as always, is that I don’t really know what I’m doing, so I certainly wouldn’t recommend this for any real projects – I’m just using it for experimentation.
Tools needed
- NodeJS
- JavaScript run-time environment
- Needed to actually run JavaScript and all the tooling
- TypeScript
- Typed language that compiles to JavaScript, comes with a compiler
- This is what we want to write!
- Babel
- JavaScript to JavaScript compiler with ability to polyfill new features into older versions
- Needed to convert the version of JavaScript we’re writing to a version browsers can execute
- Webpack
- Asset bundler – decouples your development project structure from the deliverable
- Not strictly needed, but extremely useful for any “real” project
Steps
It’ll look something like this when all done:
- Write code in TypeScript
- Use TypeScript compiler to compile TypeScript into a recent version of JavaScript, without providing backwards compatibility or browser polyfilling
- Use Babel compiler to turn recent version of JavaScript, which browsers can’t natively execute, into a version browsers can execute
- Use Webpack to grab your assortment of JavaScript files, organized however you want for development, and create a more easily deliverable “bundle” of everything
From the beginning, that means:
-
- Install NodeJS (use the latest version unless you have reason to do otherwise)
- Create your project
$ yarn init
yarn init v0.27.5
question name (typescript-front-end-seed):
question version (1.0.0):
question description: Seed project for TypeScript in the browser
question entry point (index.js):
question repository url (https://github.com/tobymurray/typescript-front-end-seed.git):
question author (Toby Murray <murray.toby+github@gmail.com>):
question license (MIT):
success Saved package.json
Done in 34.38s.
-
- Add all the dependencies we’ll need – TypeScript, Babel (note Babel by itself doesn’t really do anything, you need to include a plugin), and Webpack
$ yarn add -D typescript babel-cli babel-preset-env webpack
-
- Create whatever project structure you want. I’ll do something like
src/
for TypeScript code, andpublic/
for static files (e.g. HTML).
- Create whatever project structure you want. I’ll do something like
$ mkdir src public
$ touch public/index.html src/index.ts
-
- Create the configuration files you’ll need for all the tools –
tsconfig.json
for TypeScript,.babelrc
for Babel andwebpack.config.js
for Webpack
- Create the configuration files you’ll need for all the tools –
$ touch tsconfig.json .babelrc webpack.config.js
Tool configuration
Now comes either the interesting part or the awful part, depending on your perspective – configuring all the tools to do what we want! To keep things clear, we’ll place the output of the TypeScript compiler into a tsc folder, then we’ll feed that as input into Babel. The output of Babel will go into a babel folder. We’ll then use Webpack to consume the contents of the Babel folder and put it in a dist folder (this is what we’d actually serve up to a client browser).
TypeScript
Keeping this as simple as possible (there are plenty of options to play with), the two big decisions are what to use as the target
and module
version to use. Fortunately, we don’t really have to care too much, it just has to be consumable by Babel. To get all the features possible (mmm, delicious features), we can target e.g. ES2017
, and use commonjs
.
{
"compilerOptions": {
"target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"outDir": "./build-tsc", /* Redirect output structure to the directory. */
}
}
Babel
Again, doing as little as possible, we’ll tell Babel to do whatever it needs to do to target apparently 95% of user’s browsers. For some reason, Babel does not support setting the output directory in the configuration file (see options here), it has to be passed as an argument to the invocation of Babel.
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
Webpack
Likewise, for the start Webpack doesn’t have to be that complicated. We’ll include source maps here, don’t feel obliged to do so though.
const path = require('path');
module.exports = {
devtool: "source-map",
entry: './build-babel/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
package.json
To avoid having to remember anything, a few scripts in package.json
can be useful. Breaking them out so it’s clear what step is doing what, it could look like this:
"scripts": {
"clean": "yarn run clean-build-steps && rm -rf dist",
"tsc": "./node_modules/.bin/tsc",
"babel": "./node_modules/.bin/babel build-tsc --out-dir build-babel --source-maps",
"webpack": "webpack && cp public/* dist",
"clean-build-steps": "rm -rf build-tsc build-babel",
"build": "yarn run clean && yarn run tsc && yarn run babel && yarn run webpack && yarn run clean-build-steps"
}
Build
Running yarn build
(after the initial install) will:
- Clean anything from previous executions of the script
- This includes any leftover build artifacts, as well as the
dist
directory
- This includes any leftover build artifacts, as well as the
- Use the TypeScript compiler to take everything from the
src
directory, transpile it to ES2017 JavaScript, and output it into thebuild-tsc
directory - Use Babel to convert everything in the
build-tsc
directory from ES2017 to ES2015 and output it intobuild-babel
- Use Webpack:
- Look in the
build-babel
folder - Find index.js
- Parse
index.js
as an entrypoint, and resolve dependencies - Add everything needed into one big
bundle.js
- Look in the
- Create the “deployable” directory
- Copy the static HTML into the
dist
directory - Copy the bundle.js into the
dist
directory
- Copy the static HTML into the
Serve
With something like http-server and serving the dist
directory, we can see the product of our work!
$ http-server dist
Starting up http-server, serving dist
Available on:
http://127.0.0.1:8080
http://10.0.2.15:8080
http://172.17.0.1:8080
Hit CTRL-C to stop the server
See the GitHub repository here and the deployed example here.
Hi,
Nice Article.
Typescript files can be directly compiled into ES5 without Babel.
Thanks
You’re absolutely right. I don’t remember why I used Babel at the time. Possible reasons I can think of (without validating them):
– Babel supported more polyfills than TSC, so allowed for usage of newer features
– I may have been mucking around with a mix of TypeScript code and vanilla JavaScript, and at the time (not sure if anything has changed or this ever actually happened) I don’t think the TypeScript compiler transpiled non-TypeScript files.
I’m not sure if either of these cases ever existed, or if they did if they’re still relevant at all. Definitely for the simplest case it looks like Babel could be excluded to make this quite a bit easier!