Production-Grade TypeScript tsconfig
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: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: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: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: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: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: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.