Why Add Types to JavaScript?
Picture this: You’re happily coding in JavaScript, when suddenly - “Cannot read property ‘name’ of undefined”. Ugh, we’ve all been there! TypeScript is like having a friend who catches these mistakes before they happen.
The Origin Story
JavaScript is like Peter Parker before the spider bite - great potential, but prone to accidents. TypeScript is the spider bite that gives JavaScript superpowers. It adds a type system that helps catch bugs early and makes your code more reliable.
Your First TypeScript Adventure
Let’s start with a simple JavaScript function and transform it into TypeScript:
// JavaScript
function greet(name) {
return "Hello, " + name + "!";
}
Now, let’s add some TypeScript magic:
// TypeScript
function greet(name: string): string {
return "Hello, " + name + "!";
}
See that : string
? That’s TypeScript telling us “this function takes a string and returns a string”. Now try this:
greet(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'
TypeScript just saved us from a potential bug! 🎉
Basic Types: Your New Superpowers
Let’s explore some basic TypeScript types:
// Basic types
let heroName: string = "Spider-Man";
let age: number = 25;
let isAvenger: boolean = true;
let powers: string[] = ["web-slinging", "wall-crawling"];
// Object type
let hero: {
name: string;
age: number;
powers: string[];
} = {
name: "Spider-Man",
age: 25,
powers: ["web-slinging", "wall-crawling"]
};
Interfaces: Creating Your Own Types
Interfaces are like blueprints for objects. They’re super useful for defining the shape of your data:
interface Hero {
name: string;
age: number;
powers: string[];
catchPhrase?: string; // Optional property
}
function introduceHero(hero: Hero): void {
console.log(`I am ${hero.name}, and I'm ${hero.age} years old!`);
if (hero.catchPhrase) {
console.log(hero.catchPhrase);
}
}
const spiderMan: Hero = {
name: "Spider-Man",
age: 25,
powers: ["web-slinging", "wall-crawling"]
};
introduceHero(spiderMan);
Type Aliases: Your Custom Types
Sometimes you want to create your own type combinations:
type PowerLevel = 'rookie' | 'intermediate' | 'expert';
interface Hero {
name: string;
powerLevel: PowerLevel;
}
const batman: Hero = {
name: "Batman",
powerLevel: "expert" // TypeScript will ensure this is one of the allowed values
};
Generics: The Ultimate Flexibility
Generics are like wildcards that make your code more reusable:
function createHeroTeam<T>(members: T[]): T[] {
return [...members];
}
interface Superhero {
name: string;
power: string;
}
interface Villain {
name: string;
evilPlan: string;
}
const heroes = createHeroTeam<Superhero>([
{ name: "Iron Man", power: "Technology" },
{ name: "Thor", power: "Lightning" }
]);
const villains = createHeroTeam<Villain>([
{ name: "Thanos", evilPlan: "Collect infinity stones" }
]);
Real-World Example: A Todo App
Let’s build a simple todo app with TypeScript:
interface Todo {
id: number;
title: string;
completed: boolean;
dueDate?: Date;
}
class TodoList {
private todos: Todo[] = [];
addTodo(title: string, dueDate?: Date): void {
const todo: Todo = {
id: Date.now(),
title,
completed: false,
dueDate
};
this.todos.push(todo);
}
toggleTodo(id: number): void {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
getTodos(): Todo[] {
return this.todos;
}
}
// Usage
const myTodos = new TodoList();
myTodos.addTodo("Learn TypeScript with baransel.dev");
myTodos.addTodo("Build awesome apps", new Date("2024-10-24"));
TypeScript with React
TypeScript and React are like peanut butter and jelly. Here’s a quick example:
interface Props {
name: string;
age: number;
onSuperPower?: () => void;
}
const HeroCard: React.FC<Props> = ({ name, age, onSuperPower }) => {
return (
<div>
<h2>{name}</h2>
<p>Age: {age}</p>
{onSuperPower && (
<button onClick={onSuperPower}>
Activate Super Power!
</button>
)}
</div>
);
};
Tips and Tricks
- Start Simple: Begin with basic types and gradually add more complex ones.
- Use the Compiler: TypeScript’s compiler is your friend - pay attention to its errors.
- Don’t Over-Type: Sometimes any is okay (but use it sparingly!).
- Enable Strict Mode: Add “strict”: true to your tsconfig.json for maximum protection.
Common Gotchas and How to Fix Them
// Problem: Object is possibly 'undefined'
const user = users.find(u => u.id === 123);
console.log(user.name); // Error!
// Solution: Optional chaining
console.log(user?.name);
// Problem: Type assertions
const input = document.getElementById('myInput'); // Type: HTMLElement | null
const value = input.value; // Error!
// Solution: Type assertion or type guard
const value = (input as HTMLInputElement).value;
// or
if (input instanceof HTMLInputElement) {
const value = input.value;
}
Wrapping Up
TypeScript might seem like extra work at first, but it’s like having a superpower that helps you catch bugs before they happen. Start small, gradually add more types, and before you know it, you’ll be wondering how you ever lived without it!
Remember:
- Types are your friends
- The compiler is your sidekick
- Practice makes perfect