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

  1. Start Simple: Begin with basic types and gradually add more complex ones.
  2. Use the Compiler: TypeScript’s compiler is your friend - pay attention to its errors.
  3. Don’t Over-Type: Sometimes any is okay (but use it sparingly!).
  4. 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