TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
7: Primitive Types and Type Annotations
I was tinkering with a small RPG character creator the other day and realized I was falling into a classic JavaScript trap. I had a function to calculate a character's "Attack Power," but because I wasn't being explicit about my data, I accidentally passed a string from an input field into a math operation. It didn't crash—it just gave me a weird result like "105" instead of 15. That's exactly where TypeScript's primitive types save us.
Letting JS handle the chaos
Let's look at how I started. I wrote this simple function to add a strength bonus to a base attack value. In plain JavaScript, it looks like this:
function calculateAttack(base, bonus) {
return base + bonus;
}
console.log(calculateAttack(10, 5)); // 15
console.log(calculateAttack(10, "5")); // "105" - Oops.
See that? JavaScript just shrugged its shoulders and concatenated the numbers because it didn't know what base or bonus were supposed to be. In a large codebase, this is a nightmare to debug because the error doesn't happen at the function call; it happens later when your character suddenly does 105 damage and kills the final boss in one hit.
Putting some guardrails in place
To fix this, I'm going to use a type annotation. This is basically me telling TypeScript, "Hey, I expect this specific variable to always be this specific type." For our attack power, we need numbers.
function calculateAttack(base: number, bonus: number) {
return base + bonus;
}
Now, watch what happens when I try to pass that string again:
calculateAttack(10, "5");
// Error: Argument of type 'string' is not assignable to parameter of type 'number'.
The red squiggly line appears immediately. I don't even have to run the code to know it's broken. By adding : number, I've created a contract. If I try to break that contract, TypeScript yells at me before the code ever hits the browser.
Defining the rest of the character
A character isn't just numbers, though. We need a name and a status. Let's try to build out a small profile. This is where the other primitives come in: string for text and boolean for true/false flags.
let characterName: string = "Grog the Bold";
let isAlive: boolean = true;
let level: number = 1;
I've explicitly typed all three here. If I accidentally tried to set isAlive = "Yes", TypeScript would flag it because "Yes" is a string, not a boolean. It feels a bit verbose at first, but honestly, it's like documentation that actually enforces itself.
When to stop typing everything
Now, here is a pro tip: you don't actually have to annotate every single line. If I write let level = 1;, TypeScript is smart enough to look at the value 1 and say, "Okay, level is clearly a number." This is called type inference.
let stamina = 100; // TypeScript infers this is a number
stamina = "Empty"; // Error: Type 'string' is not assignable to type 'number'.
Even though I didn't write : number, TypeScript still locked that variable in as a number. I usually rely on inference for simple variable declarations to keep the code clean, but I always use explicit annotations for function parameters. Why? Because the function is the "entry point" for data, and that's where the most dangerous bugs sneak in.
📋 Practical Task
Fixing the Inventory System Bug
You are working on a game inventory system. A teammate wrote a function to update the quantity of an item, but they forgot to add type annotations, leading to a bug where quantities are being stored as strings instead of numbers.
Your Task:
- Modify the
updateQuantityfunction to ensure both thecurrentAmountand thechangeparameters are strictlynumbertypes. - Create a variable called
itemNameand annotate it as astring. - Create a variable called
isStackableand annotate it as aboolean. - Try to call
updateQuantitywith a string (e.g.,"5") to verify that TypeScript now catches the error.
// Current buggy code
function updateQuantity(currentAmount, change) {
return currentAmount + change;
}
// Add your typed variables here:
// itemName...
// isStackable...
// Test your function:
console.log(updateQuantity(10, 5));
There are no comments for now.