Check out a free preview of the full TypeScript Fundamentals, v3 course:
The "Compiling a TypeScript Program" Lesson is part of the full, TypeScript Fundamentals, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Mike walks through the structure of a TypeScript program, the TSC compiler CLI command, discusses how the compiler-emitted JS code changes depending on JS language level and module type, and demonstrates a program’s compiled output, including the type declaration file. A student's question regarding how to define multi-target output is also covered in this segment.

Get Unlimited Access Now

Transcript from the "Compiling a TypeScript Program" Lesson

>> The first thing we're gonna do is compile a basic TypeScript program. And this is gonna be about the simplest program that we can think of, consisting only of three files. Our goals in this chapter are to learn a little bit about how to use the TSC compiler command.

[00:00:18] We're going to learn and understand how JavaScript language level and module type affect the kinda thing that comes out of our compiler. And then, finally, we're gonna look at other things that come out of the compiler than JavaScript, right? We're gonna see some type information that we could potentially publish along with this code.

[00:00:41] If it were a library and this is called the declaration file. Which has the extension .d.ts. So let's consider a simple project, as I said, it'll consist of three files. So we've got package JSON, which is the normal package JSON you're used to seeing in JavaScript project. We have a tsconfig file, which contains all of the instructions, and options, or passing to the compiler.

[00:01:07] And then, finally, we have some source code. So let's look at these three files one by one. In package JSON, we can see a couple of things. We have only one dependency, and it's a dev dependency, and that is TypeScript. In addition to that, we have an NPM script.

[00:01:25] So that we could run something like yarn Dev, or NPM, run Dev, and we would see the TypeScript compiler start up and watch our source code. So that every time we save a file, it'll incrementally kind of update the build result. If you've used Webpack-dev-server or something like nodemon, this will give you similar behavior.

[00:01:49] And preserve watch output this just make sure that every save does not clear your console's output. You kind of wanna keep that council output, in my opinion, I don't like things erasing my terminal history. So let's look at our tsconfig, the second of the three files. We have only three properties in this file, and we're gonna start from the bottom.

[00:02:18] So include tells the compiler where to find our source code. In this case, we're going to end up compiling all ts files that we find in the source folder, that's just index ts. We have two compiler options that we're defining here. First is, where are we going to put our output, right?

[00:02:38] Everything that the compiler generates. By default, TypeScript will create JavaScript files that are side by side with the TypeScript source that created them. I don't like this because it kind of mixes my build result with my source code. I like having everything in a folder like dist or out, something like that.

[00:02:58] So that if I ever wanna clear everything away and build again, I can just rm rf and be rid of the built stuff. Finally, we have a target property, and this helps us describe the language level of our build output. By default, it's ES3, which is kind of like Internet Explorer six level of JavaScript.

[00:03:22] And we're gonna start there and see the garbage that the compiler spits out. And we're gonna crank that language level up to more and more modern JavaScript. Let's say we were only supporting modern browsers, and we'll see that things get cleaned up quite nicely. So it's important to understand that everything we described in this compiler object can be passed to the TSC command, as a COI flag.

[00:03:49] So here's an example of outDir. Now, I don't typically like using VCLI flags except in very, very simple situations. I'm very quick to sort of set up a tsconfig and have this nice, you know, configuration that I can check in with my project. And I can be sure that I'm gonna be running exactly the same command over and over again.

[00:04:15] The last of the three files we're gonna at, is the source code, right, this index.ts file. And it's not a big program, but it contains a couple interesting things that will present us with some clear signals about how the compiler responds to that target property, this here. As well as the type of module we instruct the compiler to make for us.

[00:04:40] So you're going to see that it includes the use of a promise, which only came with JavaScript 2015 edition, right, ES2015. Then we'll see that a sync and await are used, which kind of had partial support through generator functions in ES2015. But it only got real clear support like using a sync and await as keywords that came in ES2017.

[00:05:07] So we should start to see things kind of disappear and become the more normal code that we're used to reading modern JavaScript as we crank that language level up. And here is the source code. So all this program does, here's the entry point. It console.log something, and what is it console.log?

[00:05:27] Well, it's whatever addNumbers returns and that gets awaited. So addNumbers, just waits for half a second and then adds a and b together. So effectively we should see like a half second wait and then seven printed to our console, not too complicated. This is a good opportunity for me to show you one of the features of this website that we're gonna be looking at today.

[00:05:58] You can hover over various symbols in these code samples and see what you would be seeing in Visual Studio Code. So this is a little bit like a read only VS Code frame where you can really understand the types of everything that you're touching. This is really, really important because the tooltips don't lie, this will tell you why something is failing and it helps, you understand what's going on within the code.

[00:06:28] So the vast majority of the code samples, you'll see today allow you to do this. Additionally, if we want to see this code run in the TypeScript playground, you could hit this try button. And it would take you right here and you'd be able to actually click logs, and run this, and right now, we can't run it.

[00:06:47] But because we have an expert in this playground, which it's not designed to handle. But you're gonna be able to actually, play with each of these examples and I encourage you to poke at them and to understand them, and to maybe try to break them try to fix them.

[00:07:05] So let's go back to what we were just looking at. So this is our small program and here I'm cluing you into hovering over tooltips. So how might we run the compiler? Well, if you were to check this program out this this repo out what you could, you could run yarn dev, and you would see that TypeScript is starting to watch your code.

[00:07:26] And immediately you would see a dist folder created, which would contain an index.js file. And we can look at what that code would be and shield your eyes because it's a mess. This is like all the polyfills, right, this is a sync and await and promises and just a bunch of junk here, right?

[00:07:51] Remember, this is really dumbed down JavaScript for IE6. So we're gonna close this, be done with it. And that'll be our starting point, just a bunch of junk. So let's change the target language level, we can simply go from ES3 to ES2015. Leaving everything else intact, and the compiler output would change.

[00:08:17] So now we start to see some more modern language features, but not everything we're used to seeing. So there is a promise constructor which is nice, we do see a yield keyword. Interesting that we got this in ES2015, but we didn't get a sync and await yet. So we have a generator function which makes the polyfill for a sync and await makes it much cleaner.

[00:08:44] Because that's a very important thing to be able to use to define a sync and await. But it doesn't yet look like our source code with the type stripped away. That's where we're sort of working our way towards. So this is ES2015 if we step up to ES2017, this is what it would look like.

[00:09:07] So we're going to see actual use of the a sync and await keywords. And this a waiter helper, which is this thing up here, that will have disappeared and here we go. Now, this looks like our TypeScript code with the types removed. So if you're interested in how to define your output, target is a really important thing to define.

[00:09:32] One of the questions we got during the break had to do with multi target output, right? What if you're building for modern browsers and you have a legacy browser you need to support. So typically what I would do is have TypeScript output something like this, which by the way, still is in ES modules.

[00:09:53] And then you could use something like Babel to make a legacy build for IE11 if you need to. So in your dist folder, if we had run the compiler, we would have found a file called index.d.ts, and this is a declaration file. Now, there's a new keyword here that I'm gonna ask you to ignore for the time being.

[00:10:18] Cuz it's not the most interesting thing going on here. The most interesting thing is this appears to be a function with no implementation. It's just sort of a semi colon at the end of the arguments list, right? So you can think of this almost like the types that were stripped away from our source code.

[00:10:37] So you start with TypeScript, which is code that runs and types, and then your compiler almost separates those out. So you get a js file that runs and you get these dts files, which contain only the type information. Why is this good? Well, it lets people who are just using JavaScript who aren't writing TypeScript.

[00:11:02] They can compile those js files and just call it a day. And people who are writing TypeScript, they can kind of reassemble that together and have the JavaScript that runs and the types that describe the constraints. So this is what will allow you to remain compatible with users who depend on your code regardless of whether they themselves are authoring TypeScript.

[00:11:27] So one more thing that's important to understand are the types of modules the compiler is emitting. If we tried to run this code that we generated up here, right this code here, specifically with this export keyword. If we tried to run this with node, we would get an error right at that line with the export keyword, why?

[00:11:47] Node wants common j s modules right this is node js is module system predates a standardized JavaScript module. And even today, although they are moving towards these standardized modules, even today, node wants to run a common j s module. And you can read more about that if you click this link.

[00:12:11] But if we wanted to make something that runs with node, it's just as easy as adding a module property to our compiler options in the tsconfig file. And what this is gonna generate for us is it will change this export add numbers and we'll change that into exports.addNumbers equals addNumbers.

[00:12:33] So this is the kind of output that we expect in the node js world. This represents the easiest and simplest possible TypeScript program that you could compile. Now we're going to pivot and talk about the programming language. Now that we kind of know how to use the tool