20.8 Extending tsconfig: Shared Base Configurations

Right, so you’ve got more than one project. And you’re not a masochist. You don’t want to copy-paste the same 50 lines of tsconfig.json into every single frontend app, library, and random script folder. This is where extends swoops in to save you from your own maintenance nightmare. It’s the closest thing we have to configuration inheritance, and it’s glorious. The concept is simple: you define a base tsconfig.json file with all your shared, common settings. Then, in your individual project configs, you point to that base file and add the project-specific tweaks. TypeScript will effectively merge the two. It’s like a parent setting the house rules (“no anys in this house, young man!”), and the child project adding its own exceptions (“but I need "moduleResolution": "nodenext" for this one thing!”).

20.7 Project References: Splitting Large Projects

Right, so your project has grown. It happens to the best of us. One day you’re building a neat little library, the next you’re staring at a sprawling monolith of interdependent code. tsc is starting to groan under the weight, and the thought of a full rebuild every time you change a single utility file is giving you a migraine. This is where Project References come in. They’re TypeScript’s official answer to “how do I split this behemoth without losing my mind?”

20.6 outDir, rootDir, and Declaration Output

Right, let’s talk about getting your beautifully crafted TypeScript out of its cozy development directory and into a structure that something else, like Node.js or a browser, can actually run. This is where outDir and rootDir come in, and if you’re publishing a library, declarationDir joins the party. These settings feel simple until they aren’t, and then you’re staring at a cryptic error about being “not under ‘rootDir’” and questioning all your life choices. I’ve been there. Let’s fix that.

20.5 include, exclude, and files: Controlling What Gets Compiled

Right, let’s talk about the three directives you’ll use to tell the TypeScript compiler, “No, not that file. I know it has a .ts extension, but trust me, it’s a hot mess and we don’t go near it.” This is your project’s bouncer, and include, exclude, and files are the guest list. First, a crucial piece of context that everyone misses: the defaults. If you don’t specify an include array, TypeScript will, by default, grab every .ts, .tsx, .d.ts file it can find. And it will look everywhere, including node_modules (which, let’s be honest, is a digital haunted house you should never, ever compile). This is why an empty tsconfig.json is practically a declaration of chaos. You’re letting the compiler run wild in your pantry.

20.4 paths and baseUrl: Path Aliases for Clean Imports

Right, let’s talk about cleaning up your import spaghetti. You know the drill: import { Button } from '../../../../components/ui/Button';. It’s ugly, brittle, and if you move the file, your whole house of cards comes tumbling down. The designers of TypeScript felt your pain and gave us paths and baseUrl. It’s a fantastic feature, but it’s also a bit of a trap for the unwary because it’s not magic—it’s just a clever lie we tell the compiler that we have to make real for the runtime. Let’s get into it.

20.3 module and moduleResolution: How TypeScript Finds Modules

Right, let’s demystify how TypeScript finds your code. This is where most people’s brains go numb, and honestly, for good reason. The interplay between module and moduleResolution is one of the most common sources of “But it worked on my machine!” in the TypeScript world. We’re going to fix that. Think of module as you telling TypeScript what syntax you’re using (import foo from 'foo'), and moduleResolution as the strategy it uses to find the actual file that 'foo' refers to. They are a package deal. You can’t understand one without the other.

20.2 lib: Including Type Definitions for Built-In APIs

Right, let’s talk about lib. This is the setting where you tell TypeScript, “Hey, I’m planning to run my code here. These are the built-in APIs I expect to be available.” It’s how you avoid getting errors for using Promise or Map or document.querySelector, and it’s also how you get yelled at for using fetch when you’ve told TypeScript you’re writing for Node.js 12. It’s a contract, and you get to define the terms.

20.1 target: Choosing the Output JavaScript Version

Right, let’s talk about target. This is where you tell the TypeScript compiler what version of JavaScript it should aim for. It’s not a suggestion; it’s a hard constraint. Think of it less like choosing a target on a shooting range and more like telling your time-traveling car what year you want to emerge in. Set it too far back, and you’ll be writing code that has to avoid all the cool features the future invented. Set it too far forward, and you’ll crash into a browser that’s still waiting for the invention of the wheel.

— joke —

...