33.7 Measuring Progress: Counting any Usages and Coverage

Right, so you’ve started the migration. You’ve got tsc running, you’ve silenced the most egregious errors with a few any bandaids, and now you’re staring at a question: “Are we there yet?” The answer, my friend, is almost certainly “no,” but we can at least figure out how far away ’there’ is. We need metrics that tell a true story, not a fairy tale. Relying on the TypeScript compiler’s error count is like measuring a road trip by how many times you’ve screamed at other drivers—dramatic but not terribly useful. Let’s talk about real, actionable metrics.

33.6 Automating Migration with ts-migrate and TypeScript-Migrate

Right, so you’ve decided to stop living in the wild west of JavaScript and put on the structured, slightly-constricting-but-in-a-good-way suit of TypeScript. Good for you. Doing this by hand for a large codebase is a special kind of masochism I don’t recommend. This is where automation tools like ts-migrate (from Airbnb) and typescript-migrate (a similar concept) come in. They’re not magic wands, but they’re the closest thing we have to a bulldozer that can clear the initial path. Think of them as your over-caffeinated intern who does 80% of the grunt work incredibly fast, but you absolutely must check their work before you ship it.

33.5 Using @ts-ignore and @ts-expect-error as Temporary Suppressions

Alright, let’s talk about the duct tape and baling wire of your TypeScript migration: @ts-ignore and its slightly more responsible cousin, @ts-expect-error. You’re going to use these. I’ve used them. We all have. They are the temporary barricades you throw up against the tsunami of red squiggles so you can actually compile your code and keep the business running while you methodically fix things. The key word here is temporary. Think of them as a “Check Engine” light you’re actually going to get around to fixing, not one you just put a piece of electrical tape over.

33.4 Dealing with Untyped Dependencies During Migration

Right, so you’ve started wrapping your own code in types, feeling that sweet, sweet intellisense flow, and then you hit this wall: import someLibrary from 'that-old-untyped-package'. Suddenly, your beautifully typed file is awash in any. It’s like meticulously cleaning your house only to have your roommate drag in a muddy dog. Don’t panic. This is a rite of passage. We’re going to tame that muddy dog, or at least build it a very specific shed so it doesn’t track mud everywhere.

33.3 Renaming Files: .js to .ts One Module at a Time

Right, let’s get our hands dirty. We’re not doing a big-bang rewrite where you rename every single file at once and pray for the best. That’s a fantastic way to ruin your entire weekend. Instead, we’re going to do this surgically, one module at a time. This is the “eat the elephant one bite at a time” strategy, except the elephant is your technical debt and we have a very precise fork.

33.2 Adding tsconfig.json to an Existing Project

Right, let’s get this party started. You’ve got a sprawling JavaScript project, and you’ve decided to inject some sanity into it with TypeScript. The very first, and arguably most important, step is to drop a tsconfig.json file in the root of your project. This file is the mission control for the TypeScript compiler (tsc); it tells it exactly how to behave, what files to look at, and how strictly to judge your life choices. Think of it less as a configuration file and more as a rulebook you get to write for your new, more disciplined coding life.

33.1 The Incremental Migration Strategy: allowJs and checkJs

Right, so you’ve decided to stop living in the wild west of JavaScript and put on the structured, slightly-stiff-but-incredibly-warm jacket of TypeScript. Good choice. But you’re not about to stop the entire product roadmap, lock the team in a basement for six months, and rewrite 200,000 lines of code in one go. That’s a fantastic way to get fired, or at the very least, develop a nervous twitch. We’re going to be smart about this. We’re going to use TypeScript’s built-in escape hatches and incremental flags. This isn’t an all-or-nothing proposition. The two key players in this strategy are allowJs and checkJs. Think of them as the training wheels and the slightly anxious parent running behind the bike, respectively.

90.9 Staying Current: Tracking PEPs and the Python Changelog

Right, so you’ve decided not to just be a tourist in Python-land. You want to be a citizen. That means keeping up with the news. And in Python, the news doesn’t come from a sketchy algorithm; it comes from two very specific, very nerdy sources: the Python Enhancement Proposals (PEPs) and the official changelog. Ignoring these is like trying to bake a cake without checking if you’ve got eggs—you might get something, but it’s probably going to be a flat, disappointing mess.

90.8 Python 3.13: Free-Threaded Mode (Experimental) and JIT Compiler

Alright, let’s pull back the curtain on the two biggest party tricks in Python 3.13: the free-threaded mode and the JIT compiler. Before you get too excited, let’s be clear: these are experimental, bleeding-edge features. This means they’re here for us to poke, prod, and break, not to bet your company’s entire data pipeline on just yet. But they represent a fundamental shift in Python’s trajectory, and you need to understand them because this is the future, whether we like it or not.

90.7 Python 3.12: f-String Improvements, Type Parameter Syntax

Right, let’s talk about the two headline features in Python 3.12 that are actually useful for your day-to-day coding life, not just academic curiosities. The core devs finally gave f-strings the steroids we’ve been begging for, and they introduced a new, cleaner syntax for generics that will make your type hints look less like a cat walked across your keyboard. f-Strings Get a Massive Upgrade You already know f-strings. They’re the reason we all stopped using the clunky .format() method for 90% of our string formatting. But they had a few annoying limitations. 3.12 fixes the big ones, and it’s glorious.

90.6 Python 3.11: Exception Groups, tomllib, Faster Interpreter

Right, let’s talk about Python 3.11. This is the release where the core team looked at the interpreter, saw it was good, but decided “good” wasn’t good enough. They went at it with a performance profiler and a mandate to make things fast. But they also snuck in a few language features that are, frankly, a bit of a big deal. We’ll get to the speed in a minute, but we have to start with the headliner: a better way to handle multiple errors at once. Because sometimes, failure isn’t a single event; it’s a whole catastrophe.

90.5 Python 3.10: match/case, Better Error Messages

Alright, let’s talk about Python 3.10. This is where things started to get genuinely fun again. For years, we’d been dutifully writing if/elif/elif chains that looked like a sad, tangled string of Christmas lights. Then, the language designers finally gave in and gave us what every other self-respecting modern language had: structural pattern matching, or as we call it, match/case. It’s not a switch statement. Don’t call it that. It’s so much more, and also, in some ways, a little less. Let’s dive in.

90.4 Python 3.9: Dictionary Union, Type Hint Generics in Built-ins

Alright, let’s talk about Python 3.9. This is where the core devs finally looked at some of our most common, grunt-work code patterns and said, “Yeah, we can fix that.” It’s a release that feels less like a grand new vision and more like a highly competent engineer finally getting around to the quality-of-life improvements we’d all been begging for. Two features stand out: dictionary union operators and the ability to use generics in standard collection type hints. Both are deceptively simple-looking changes that will save you from a surprising amount of boilerplate and outright ugliness.

90.3 Python 3.8: Walrus Operator, Positional-Only Parameters

Alright, let’s get into the good stuff. Python 3.8 landed in late 2019, and it brought a couple of features that were genuinely useful, not just the usual under-the-hood optimizations that make you nod politely and then forget about. We’re talking about the Walrus Operator and Positional-Only Parameters. One is a syntactic sugar rush, the other is a quiet but powerful enforcer of API clarity. Let’s break them down. The Walrus Operator (:=) First, the one that caused a lot of fuss and some truly terrible aquatic mammal puns: the Walrus Operator, officially known as the Assignment Expression. Its job is simple but profound: it allows you to assign a value to a variable inside an expression. The reason it looks like := is because, well, look at it sideways. It’s a walrus. Let’s move on before I start making seal jokes.

90.2 Migrating a Python 2 Codebase to Python 3

Alright, let’s get our hands dirty. Migrating from Python 2 to 3 isn’t just a version bump; it’s a language transplant. The core DNA is the same, but a lot of the organs work differently. The good news? The Python community has poured an immense amount of effort into making this as painless as possible. The bad news? If your codebase is large and ancient, “painless” is a relative term. We’re going to methodically break this beast down.

90.1 Python 2 vs Python 3: The Breaking Changes

Alright, let’s get into the weeds. The Python 2 vs. Python 3 schism wasn’t just an update; it was a fundamental, “break-everything-for-the-greater-good” level rewrite. The core dev team, led by the BDFL (Guido van Rossum), decided that certain warts in the language were too ugly to live with anymore. They chose a hard break to clean things up, knowing full well it would cause years of migraines. And oh boy, did it. But they were right. Python 3 is a cleaner, more consistent language. Our job now is to navigate the minefield they left behind so you don’t have to.

— joke —

...