7.6 Combining Literals with Union Types for Discriminated Unions

Now we get to the good stuff. You’ve seen literal types and union types on their own, but when you combine them, you unlock one of TypeScript’s most powerful patterns: the discriminated union. The name sounds fancy, but the concept is beautifully simple. It’s how we tell TypeScript, “Look, this object could be one of several shapes, and here’s the literal value you can check to know exactly which one it is.”

7.5 const Assertions: as const and Deeply Readonly Inference

Alright, let’s talk about const assertions. You’ve probably used const for variable declarations to stop yourself from reassigning a primitive value. It’s like putting a child lock on a single cabinet. const myName = "Dave"; // type is "Dave", not string const answer = 42; // type is 42, not number But what about when you want to do that for an entire object or array? You might think, “I’ll just declare it with const,” but you’d be wrong, and I don’t blame you. The const keyword in a variable declaration prevents reassignment of the variable itself, but it does nothing to make the contents of an object or array immutable. The type system still widens those values to their general types.

7.4 Literal Type Widening: When TypeScript Broadens Literals

Right, so you’ve just started using literal types and you’re feeling clever. You’ve written const direction = 'left' and you see that beautiful type 'left' instead of string in your editor. You think, “I’ve got this. TypeScript understands exactly what I mean.” And then you do this: let myDirection = 'left'; // ^? let myDirection: string Wait, what? Why is it string now? I just told it it was 'left'! Welcome to your first encounter with TypeScript’s slightly overzealous but well-intentioned habit: literal type widening. It’s the language’s way of saying, “I trust you… but not that much.”

7.3 Template Literal Types: Building Strings at the Type Level

Alright, let’s talk about template literal types. You know how in JavaScript you can use template literals to build strings like `Hello, ${name}`? Well, TypeScript’s type system saw that and said, “Hold my beer.” Template literal types let you do that exact same thing, but at the type level. It’s string interpolation for your types, and it’s both brilliant and, at times, utterly unhinged. Think of them as the logical next step after union types and literal types. You have a type "admin", you have a type "user", and you can now mash them together with other string parts to create new, specific string literal types. This is how you stop dealing with string and start dealing with "user_profile_updated_event".

7.2 Numeric and Boolean Literal Types

Now, let’s get literal. You’ve seen how TypeScript lets you slap a string type on a variable and call it a day. But sometimes, you don’t just want a string; you want that specific string. You don’t want a number; you want the number 42. This is where literal types come in, and they are the secret sauce for writing code that’s not just type-safe, but *value-safe`. Think of a literal type as locking a variable to one specific, allowed value. It’s the difference between saying “this must be a road” and “this must be the I-95, and heaven help you if you end up on the I-93 instead.”

7.1 String Literal Types: "left" | "right" | "center"

Let’s talk about one of TypeScript’s most deceptively simple superpowers: string literal types. You’ve probably written code where a variable could only be a handful of specific strings, like "left", "right", or "center" for alignment. In plain JavaScript, you’d just use a string and pray to the programming gods that no one types "leeft". You’d write a bunch of unit tests and runtime checks to handle the inevitable typos. It’s a tedious, error-prone mess.

— joke —

...