Check out a free preview of the full Production-Grade TypeScript course:
The "tsconfig" Lesson is part of the full, Production-Grade TypeScript course featured in this preview video. Here's what you'd learn in this lesson:

Mike walks through common settings used in the tsconfig.json file. Setting the rootDir property omits the source directory from being included in the output directory. Using properties like noUnusedLocals and noUnusedParameters adheres to coding standards and can be more consistent than linting rules.

Get Unlimited Access Now

Transcript from the "tsconfig" Lesson

>> The next thing we're gonna do is set up TypeScript. So we've already installed TypeScript, and I need a tsconfig. That's the next thing I need to do. TypeScript allows us to set one up pretty easily. So we can do tsc or yarn tsc. This is only necessary if you don't have volta, right?

[00:00:24] Cuz earlier we showed that tsc it's sort of the right version with volta even without using that yarn, but this will always work. So tsc -- init. This will create a boilerplate standard tsconfig with some nice comments in here. Now, pro-tip, if you see a bunch of red squiggles in this file for some reason, that's because comments are not strictly allowed in JSON.

[00:00:54] So that's why if you look at your different language modes in Visual Studio code, you have JSON and then JSON C, C is for comment. So that's how you would fix that. You'd VS code, please regard this file as a JSON C file. We're gonna have to make a few changes here because frankly, this is a little bit too permissive of a tsconfig for professional use.

[00:01:26] Especially for creating something where we're writing TypeScript code from the start. So there's no advantage in a sort of starting out loose and then cranking things up incrementally. If we were converting from JavaScript to TypeScript, that's exactly what we wanna do. But I wanna start by setting the bar high.

[00:01:47] So target, this property here is the language level that we wish to support, right? So this is what we're compiling out to is not necessarily the language level that is allowed in your code. This is what is your output, right? So ES2018, for example, will include a sync and await.

[00:02:14] It won't try to collapse that down and make it so it runs even in IE11. That's what target is all about. Module, this is the output module type. We wanna change a couple other things here. So our output directory, we're gonna look for out for outDir, here it is.

[00:02:33] And I want this to be my dist folder. Why? Because by default, TypeScript will put the compiled output JavaScript and the declaration file, as siblings of the TypeScript input file. This makes it more complicated than I like if I wanna build output away, get rid of it, and sort of start clean.

[00:02:55] It's kinda all mixed together in my source folder. So I like having a folder that I can just rm, rf, get rid of it. We've got our source and we better build output separate. So it's all gonna end up in the dist folder here. rootDir, so this is important.

[00:03:14] We're gonna set this to source. This controls the output, the sorry, the folder structure within the dist folder. So it means that if I have source index.ts, I'm gonna have dist index.js. I don't want a source folder as a sub folder of dist. And this is how I would address that.

[00:03:39] At the bottom here, I'm gonna add include. So this is where we're saying, I want to include all files within my source folder. Now is a good time by the way to, oops, to make that folder, make dir source. [COUGH] So just follow the notes here, we've got the right target, we've got the right module type.

[00:04:15] Okay, two things I wanna disable here. We've got esModuleInterrupt. So I'll talk a bit more about this later. But we wanna disable this, it's disabled by default. But we wanna opt out of this, and there's a skipLibCheck down here. So these two things, I'll tell you what they do.

[00:04:39] esModuleInterrupt, this allows you to treat common.js modules as if they have a default esModuleExport, like export default. Common.js modules, where you're using like module.exports, they don't have export default. So this enables support for that. Unfortunately, if your types only work when this flag is turned on, and you have a library, anyone who consumes this library must also turn this on.

[00:05:16] And I don't like to force this decision on consumers of my libraries, there are workarounds. So we can still leave choices for people who use this library as long as we leave this disabled. skipLibCheck this is, [COUGH] so it does not check declaration files. Let me say this a different way.

[00:05:43] If things are broken in the types you consume to build this library, those errors will be ignored. Now, this might be appropriate to enable for an app, I would argue probably not even there. But certainly don't wanna enable it for a library, I would think. Given that if you hit errors here and you're forced to flip this on and say, don't type check my node modules types.

[00:06:13] Look, any types you find in node modules, don't worry about type checking those. If you have to turn this on in order to get things to work, anyone who consumes your library is likely to have to turn this on as well. So I call these like viral options in that if you flip these switches for your library, you're kind of infecting any app that consumes this library.

[00:06:35] They have to turn this on now, and there are good reasons to wanna leave them off. So I would prefer to leave them off and see how far we can get, turns out nothing's really in our way. Next thing I wanna do is, I'm looking for declaration. Here it is.

[00:06:52] So because we're building a library, I wanna make sure that we're generating those DTS files, these type information files. So we'll turn that on. And then finally, let's talk about the the sort of strictness settings. So we've got this strict to true. Now here's an interesting question for the class.

[00:07:13] Who here feels they have a very good handle on what exactly strict mode entails? When you say strict to, hands up if you feel like you could name every option that it affects? I see zero hands. And I'm not putting my hand up either, because this changes from release to release.

[00:07:36] And it turns out that like one reasonable assumption you can make is, it's all the options that begin with the word strict. But it turns out, noImplicitAny, nImplicitThis, and alwaysStrict, they're also turned on when you say strict to true. So if you go look in the notes, and actually, when we dive into this a little bit deeper, there's a link later on when we start talking about migrating from JavaScript to TypeScript.

[00:08:08] I'd like you to the actual place in the source code where you can see exactly for a given version of the compiler, what strict actually means. So I'll tell you what this means right now, and it's in the notes. This is for TypeScript 4.0, at least this is what strict mode means.

[00:08:25] But if you're using a newer version, it could mean something different. So this is noImplicitAny, noImplicitThis, strictNullChecks, FunctionTypes, BindCallApply, and PropertyInitialization. And then alwaysStrict, that's not really an enforcement mechanism in terms of like cranking up the type checking. It's a use strict in the JavaScript sense, meaning certain language features are only available in strict mode JavaScript.

[00:08:57] And so we're saying we're always using that mode, like everything is gonna be in a use strict JavaScript context. So we'll start with strict true. And then I like some of these these things that have been added recently, like noUnusedLocals and Parameters. So I could have my linter pick up on these, right?

[00:09:19] There certainly es lint rules that pick these things up, in particular this noUnusedParameters option. I feel like TypeScript support is a bit better at default configuration. In that you can use underscores for unused function arguments, and that's taken as like this is not unused, it's necessary, cuz I want the third argument of the function, right?

[00:09:47] So you can use patterns found in other languages like a leading underscore to suppress these warnings. Whereas with es lint requires a little bit more of a tricky configuration to get that kind of thing. So I let TypeScript do all of this unused detection stuff. And so we'll enable those three.

[00:10:11] And then let's see, is this even in the files? So there's a secret, this is like in and out burger here. We have the super secret menu, stripInternal. This is not part of their template. So what stripInternal does is if we use a js doc annotation at internal on a function or something or anything, right?

[00:10:41] That type information will be carved out of the declaration file. So maybe there's a function that we export for type, sorry, for testing purposes, like an internal function, but we still wanna unit test it. So we have to export it, so our types can import it. But this one sure that no consumers of the library even see that.

[00:11:02] It's stripped out, if we mark it as internal, stripInternal. This is a new option forceConsistentCasingInFileNames. This is important if you're working on Windows where the case of different cases in your file system like capital A App, or lowercase app is all pointing to the same place. So having this flag enabled is a good idea.

[00:11:36] So that you don't end up creating things on a Mac that just will never work on Windows. Because Windows thinks that it ignores case when dealing with files. One last check here, one last modification, and it's this, types. So this is important. This is, and I wish it was a little more powerful than it is right now.

[00:12:02] But here's what I'm trying to accomplish by doing this. By default, anything found in node modules/at types, right? All of that is available for auto import when I'm writing my TypeScript code. And if any of that type information makes modification to the global scope, right? And I'm talking about like window in a browser or process in a node application, all of that will be applied.

[00:12:36] So if I have just installed here, I have just types and modifications to the global scope in my source code, not just in my test code. I wanna have pretty strict control over what I'm allowed to use in my source folder. And I wish that I had some more powerful ways of banning the use of certain kinds of things in my source folder.

[00:13:03] Ideally, it should be, if it's dev dependency, it's probably not gonna be there at runtime. Just cuz I have some things that are needed for the builds, that are needed for running my tests. That's not part of my library. So I shouldn't have access to those types. So I'd like to start with a clean slate here.

[00:13:24] Now, I can still explicitly import them, but they will be available in autocomplete. And they won't touch the global scope, if I leave this as an empty array. Last thing I wanna do here is, Make a source file. So I'm just gonna make index.ts. If you're following along in the notes, there is some content that you can just copy and paste.

[00:13:54] So I'm just gonna grab that from the notes themselves. And here they are. It's just a couple functions like some calculator functions. We're ready to kinda kick the tires. And let's see if this works. Let's try yarn build. Before I run this command, note that I have no dist folder.

[00:14:26] Not yet. All right, so now I do have a dist folder. And as promised, there's no source sub folder in here. So I'll show you what this would have looked like if I did not use this rootDir option. If I comment this out and rebuild, Maybe things are a bit smarter than I thought these days.

[00:15:05] Let's set it to the home directory. There we go. So at least for some versions of the compiler, the default root directory is your Git repos top level folder. And you see the shape of your dist directory. So these are here from the previous builds. We can delete those.

[00:15:28] But now, this is not an ideal structure of your output, like that source folder is not doing anything for users. So that's why we had this option here. Just so you understand what that does. All right, and let's look at our output. So we've got our JavaScript. And we can see that this has obviously been converted to a common.js module, right?

[00:16:03] Cuz we've got exports dot something. Everything else is maintained, and if we look at our declaration file, the documentation is in there, but we just have the function types without the bodies. And I want you to note, we've got average. I can show you this outline here, average in sum3.

[00:16:26] But if we look at our JavaScript folder, sum2 is still in there. So what you're seeing is that internal function, which I may have needed to export so that I can test it. It's not part of the type information now. So in theory, someone could import it, but I'm not suggesting that they import it.

[00:16:47] I'm not giving them an autocomplete. So it's kind of on them. I can lead with an underscore or something to make it even more clear.