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
15: Typing Function Parameters and Returns
How do I actually tell TypeScript what types my function arguments should be?
It's pretty straightforward: you add the type annotation right after the parameter name in the parentheses. If you don't do this, TypeScript often defaults to any, which basically turns off the type-checker for that variable—and that's exactly what we're trying to avoid.
Let's say we're building a small system to calculate the total price of an item including tax. You want to make sure nobody accidentally passes a string into your math logic.
function calculateItemTotal(price: number, taxRate: number): number {
return price + (price * taxRate);
}
// This works great
calculateItemTotal(100, 0.07);
// TypeScript will yell at you here before you even run the code
calculateItemTotal("100", 0.07);
By being explicit here, you're creating a contract. Anyone else on your team (including "future you" in three months) knows exactly what this function expects without having to dig through the implementation details.
Do I really need to type the return value, or does TypeScript just figure it out?
Technically, TypeScript is pretty smart. It uses something called "inference" to guess the return type based on the return statement. For a simple one-liner, it's usually spot on. But I strongly recommend typing your returns anyway, especially in professional projects.
Why? Because it prevents "accidental" returns. If you tell TypeScript the function must return a number, but you accidentally return a string or a boolean due to a logic error, TypeScript will catch it immediately. If you rely on inference, TypeScript might just change the inferred type of the function, and the bug will travel further down your call stack before it finally crashes.
// Explicitly stating this returns a string
function formatCurrency(amount: number): string {
// If I accidentally returned 'amount' here without formatting,
// TS would throw an error because it's a number, not a string.
return `$${amount.toFixed(2)}`;
}
What if my function could return a few different types of things?
This happens all the time. The most common scenario I run into is a "find" function: it either returns the object you're looking for, or it returns null because the object doesn't exist. You handle this using a Union Type (the | pipe symbol).
Here is a real-world example of searching a product list:
interface Product {
id: string;
name: string;
}
const inventory: Product[] = [
{ id: 'a1', name: 'Mechanical Keyboard' },
{ id: 'b2', name: 'Gaming Mouse' }
];
function findProductById(id: string): Product | null {
const product = inventory.find(p => p.id === id);
return product ? product : null;
}
const item = findProductById('z9');
// Now TypeScript forces you to check if 'item' is null
// before you try to access item.name. This saves you from the dreaded "cannot read property of null" error.
How do I handle parameters that aren't always required?
You don't want to force the user to pass undefined or null just to satisfy the compiler. Instead, you use the optional parameter syntax by adding a question mark ? after the parameter name. Just remember: optional parameters must come after required ones.
Let's add a discount feature to our total calculator:
function calculateFinalPrice(price: number, discount: number = 0): number {
return price - discount;
}
// Wait, I actually prefer optional parameters over defaults
// when the logic changes based on presence:
function applyCoupon(price: number, couponCode?: string): number {
if (couponCode === 'SAVE10') {
return price * 0.9;
}
return price;
}
applyCoupon(100); // Valid
applyCoupon(100, 'SAVE10'); // Also valid📋 Practical Task
Exercise: Refactoring the Order Summary Generator
You've been handed a piece of legacy JavaScript that has been converted to TypeScript, but it's currently using any everywhere. Your goal is to make it type-safe.
The Requirements:
- Create an interface called
Orderwithid (string),amount (number), andcustomerName (string). - Refactor the
getOrderStatusfunction:- It should take an
id (string)as a parameter. - It should return either an
Orderobject ornullif not found.
- It should take an
- Refactor the
formatOrderSummaryfunction:- It should take an
Orderobject as the first parameter. - It should take an optional
currencySymbol (string)as the second parameter (defaulting to '$' if not provided). - It must return a
string.
- It should take an
// Start with this messy code and fix the types:
function getOrderStatus(id) {
const orders = [{ id: '101', amount: 50, customerName: 'Alice' }];
return orders.find(o => o.id === id);
}
function formatOrderSummary(order, currencySymbol) {
const symbol = currencySymbol || '$';
return `${order.customerName} spent ${symbol}${order.amount}`;
}There are no comments for now.