Introduction to TypeScript

What is TypeScript?

TypeScript is a strongly typed programming language developed and maintained by Microsoft. It was first released in October 2012 and has since gained significant popularity in the web development ecosystem. TypeScript is a superset of JavaScript, meaning that any valid JavaScript code is also valid TypeScript code. However, TypeScript extends JavaScript by adding static type definitions and other language features.

The key innovation of TypeScript is its type system. While JavaScript is dynamically typed, TypeScript adds optional static typing that allows developers to define the types of variables, function parameters, return values, and more. This type information is used during development to catch errors early but is removed during compilation, resulting in clean JavaScript that runs anywhere JavaScript runs.

// JavaScript
function add(a, b) {
  return a + b;
}

// TypeScript
function add(a: number, b: number): number {
  return a + b;
}

Why TypeScript?

1. Enhanced Developer Experience

TypeScript dramatically improves the developer experience through better tooling:

  • Intelligent Code Completion: IDEs can provide more accurate suggestions based on type information.
  • Real-time Error Detection: Catch errors before running your code.
  • Refactoring Support: Confidently rename variables, functions, and methods.

Type Information

// Without type information, the IDE doesn't know what properties 'user' has
function greetUser(user) {
  console.log("Hello, " + user.name);
}

// With TypeScript, you get autocompletion and errors if properties are missing
interface User {
  name: string;
  email: string;
  role: string;
}

function greetUser(user: User) {
  console.log("Hello, " + user.name);
}

2. Improved Code Quality and Maintainability

Types serve as built-in documentation that makes code easier to understand and maintain:

  • Self-Documenting Code: Types describe what kind of data is expected.
  • Safer Refactoring: The compiler catches potential issues when you change your code.
  • Better Collaboration: Team members can understand each other's code more easily.
// JavaScript - Not clear what format 'date' should be in
function formatDate(date) {
  // Is date a string? A Date object? A timestamp?
  // How should we handle invalid inputs?
}

// TypeScript - Clear expectations and error handling
function formatDate(date: Date | string): string {
  let dateObject: Date;

  if (typeof date === "string") {
    dateObject = new Date(date);
    if (isNaN(dateObject.getTime())) {
      throw new Error("Invalid date string provided");
    }
  } else {
    dateObject = date;
  }

  return dateObject.toISOString().split("T")[0];
}

3. Scalability for Large Applications

TypeScript excels at managing complexity in larger applications:

  • Interface Contracts: Define clear boundaries between components.
  • Type Safety Across Modules: Ensure consistency across your application.
  • Early Error Detection: Find bugs at compile time rather than runtime.
// Defining a clear contract for working with user data
interface User {
  id: number;
  username: string;
  email: string;
  isActive: boolean;
  lastLogin?: Date; // Optional property
}

interface AuthService {
  login(email: string, password: string): Promise<User>;
  logout(userId: number): Promise<void>;
  getCurrentUser(): User | null;
}

class ApiAuthService implements AuthService {
  private currentUser: User | null = null;

  async login(email: string, password: string): Promise<User> {
    // Implementation details...
    const response = await fetch("/api/login", {
      method: "POST",
      body: JSON.stringify({ email, password }),
      headers: { "Content-Type": "application/json" },
    });

    if (!response.ok) {
      throw new Error("Authentication failed");
    }

    this.currentUser = await response.json();
    return this.currentUser;
  }

  async logout(userId: number): Promise<void> {
    // Implementation details...
    await fetch("/api/logout", {
      method: "POST",
      body: JSON.stringify({ userId }),
      headers: { "Content-Type": "application/json" },
    });

    this.currentUser = null;
  }

  getCurrentUser(): User | null {
    return this.currentUser;
  }
}

4. Gradual Adoption

Unlike some technologies that require a complete rewrite, TypeScript can be introduced gradually into existing JavaScript projects:

  • Progressive Implementation: Add types to your codebase one file at a time.
  • JavaScript Interoperability: Use TypeScript with existing JavaScript libraries.
  • Adjustable Type Checking: Configure how strict the type system should be.

Overview of TS config

// tsconfig.json
{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "allowJs": true, // Allow JavaScript files to be compiled
    "checkJs": true, // Type check JavaScript files
    "strict": false, // Start with less strict type checking
    "noImplicitAny": false // Don't require explicit types for all variables
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

5. Access to Modern JavaScript Features

TypeScript allows you to use the latest JavaScript features while targeting older browsers:

  • Downleveling: Write modern code that gets compiled to support older environments.
  • Polyfill Management: Easier integration with tools like Babel.
  • ECMAScript Compatibility: Stay current with the latest JavaScript standards.
// Modern JavaScript/TypeScript
const numbers = [1, 2, 3, 4, 5];

// Using arrow functions and array methods
const doubled = numbers.map((n) => n * 2);
const evenNumbers = numbers.filter((n) => n % 2 === 0);

// Using optional chaining
const user = {
  profile: {
    settings: {
      theme: "dark",
    },
  },
};

// This won't throw an error if any property in the chain is undefined
const theme = user?.profile?.settings?.theme;

// TypeScript will compile this to compatible code for older browsers

TypeScript vs. JavaScript: Understanding the Relationship

TypeScript is not a replacement for JavaScript but rather an enhancement. It's important to understand their relationship:

TypeScript is a Superset of JavaScript

All valid JavaScript code is also valid TypeScript code. This means that:

  1. You can rename a .js file to .ts and it will work (with some exceptions).
  2. You can gradually add type annotations to existing JavaScript code.
  3. TypeScript adds features to JavaScript but doesn't remove any functionality.
// This is valid JavaScript and valid TypeScript
function sayHello() {
  console.log("Hello, world!");
}

// This is valid TypeScript but not valid JavaScript
function greet(name: string) {
  console.log(`Hello, ${name}!`);
}

TypeScript is a Development Tool

TypeScript exists primarily as a development tool. The type system is erased during compilation, resulting in plain JavaScript that runs in browsers and Node.js environments.

// TypeScript code with type annotations
function calculateArea(width: number, height: number): number {
  return width * height;
}

// Compiles to JavaScript without type annotations
function calculateArea(width, height) {
  return width * height;
}

TypeScript's Compilation Process

TypeScript code is transpiled (converted) to JavaScript through the TypeScript compiler tsc. This process:

  1. Checks the code for type errors
  2. Removes type annotations
  3. Converts newer JavaScript features to equivalent code compatible with the target JavaScript version
  4. Generates source maps for debugging (optional)
# Compile a TypeScript file to JavaScript
tsc app.ts

# Compile with specific options
tsc --target ES5 --module commonjs app.ts

# Watch for changes and recompile automatically
tsc --watch app.ts

Core TypeScript Concepts

While you'll explore most TypeScript features in depth in subsequent modules, it's helpful to understand some core concepts upfront.

Type Annotations

Type annotations are the most basic way to define types in TypeScript:

Type annotations

// Basic type annotations
let name: string = "Alice";
let age: number = 30;
let isStudent: boolean = true;
let hobbies: string[] = ["reading", "swimming", "coding"];
let tuple: [string, number] = ["coordinates", 42];

// Function with type annotations
function multiply(a: number, b: number): number {
  return a * b;
}

// Object type annotation
let person: { name: string; age: number } = {
  name: "Bob",
  age: 25,
};

Type Inference

TypeScript can often infer types without explicit annotations:

Type inference

// TypeScript infers these types automatically
let name = "Alice"; // type: string
let age = 30; // type: number
let isStudent = true; // type: boolean
let numbers = [1, 2, 3]; // type: number[]

// Type inference for function return types
function add(a: number, b: number) {
  return a + b; // Return type inferred as number
}

// Type inference for complex objects
let person = {
  name: "Bob",
  age: 25,
  address: {
    street: "123 Main St",
    city: "Anytown",
  },
};
// TypeScript infers the complete structure of this object

Interfaces

Interfaces define the shape of objects. Use interfaces when:

  • Defining object shapes
  • Api contracts
  • Leveraging of declaration merging

Interfaces

// Interface definition
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // Optional property
  readonly createdAt: Date; // Can't be modified after creation
}

// Using the interface
function createUser(userData: User): User {
  // Implementation...
  return userData;
}

const newUser: User = {
  id: 1,
  name: "Alice",
  email: "alice@example.com",
  createdAt: new Date(),
};

Type Aliases

Type aliases create named types that can be reused. Use types when:

  • You need unions
  • Complex intersections
  • Working with tuples
  • Using mapped types
  • Ensuring immutability of the definition
  • Alias of primitive types

Type aliases

// Simple type alias
type ID = string | number;

// More complex type alias
type Point = {
  x: number;
  y: number;
};

// Using type aliases
function printID(id: ID) {
  console.log(`ID: ${id}`);
}

function calculateDistance(p1: Point, p2: Point): number {
  return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
}

Union and Intersection Types

Union types allow a value to be one of several types, while intersection types combine multiple types into one:

Union / Intersection types

// Union type: can be either a string or a number
function formatValue(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  return `$${value.toFixed(2)}`;
}

// Intersection type: combines properties from multiple types
type Employee = {
  id: number;
  name: string;
};

type Manager = {
  employees: Employee[];
  department: string;
};

type ManagerWithDetails = Employee & Manager;

const director: ManagerWithDetails = {
  id: 1,
  name: "Alice",
  employees: [{ id: 2, name: "Bob" }],
  department: "Engineering",
};

Generics

Generics allow you to create reusable components that work with a variety of types:

Generics

// Generic function
function identity<T>(arg: T): T {
  return arg;
}

const stringResult = identity<string>("hello"); // Type: string
const numberResult = identity(123); // Type: number (inferred)

// Generic interface
interface Box<T> {
  value: T;
}

const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 42 };

// Generic class
class Queue<T> {
  private items: T[] = [];

  enqueue(item: T): void {
    this.items.push(item);
  }

  dequeue(): T | undefined {
    return this.items.shift();
  }
}

const numberQueue = new Queue<number>();
numberQueue.enqueue(1);
numberQueue.enqueue(2);
const nextItem = numberQueue.dequeue(); // Type: number | undefined

TypeScript in the Real World

To appreciate the value of TypeScript, it's helpful to understand its place in the web development ecosystem.

TypeScript Adoption

TypeScript has seen widespread adoption in the industry:

  • Major Frameworks: Angular is built with TypeScript, and React and Vue.js have excellent TypeScript support.
  • Large Companies: Microsoft, Google, Airbnb, Slack, and many others use TypeScript in production.
  • Popular Libraries: Many JavaScript libraries provide TypeScript type definitions.

TypeScript in Modern Web Development

TypeScript integrates well with modern web development tools and workflows:

// React component with TypeScript
import React, { useState, useEffect } from 'react';

interface User {
  id: number;
  name: string;
  email: string;
}

interface UserProfileProps {
  userId: number;
  showEmail?: boolean;
}

const UserProfile: React.FC<UserProfileProps> = ({
  userId,
  showEmail = false
}) => {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);

        if (!response.ok) {
          throw new Error('Failed to fetch user data');
        }

        const userData: User = await response.json();
        setUser(userData);
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Unknown error');
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!user) return <div>No user found</div>;

  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
      {showEmail && <p>Email: {user.email}</p>}
    </div>
  );
};

export default UserProfile;

TypeScript and Backend Development

TypeScript is not limited to frontend development; it's also popular for Node.js applications:

// Express.js API with TypeScript
import express, { Request, Response, NextFunction } from "express";
import { body, validationResult } from "express-validator";

interface User {
  id: number;
  username: string;
  email: string;
}

const app = express();
app.use(express.json());

// In-memory user database (for demonstration)
const users: User[] = [];
let nextId = 1;

// Create user endpoint with validation
app.post(
  "/api/users",
  [
    body("username").isString().trim().isLength({ min: 3 }),
    body("email").isEmail().normalizeEmail(),
  ],
  (req: Request, res: Response) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { username, email } = req.body;

    // Check if email already exists
    if (users.some((user) => user.email === email)) {
      return res.status(409).json({
        error: "A user with this email already exists",
      });
    }

    const newUser: User = {
      id: nextId++,
      username,
      email,
    };

    users.push(newUser);

    return res.status(201).json(newUser);
  }
);

// Get user by ID endpoint
app.get("/api/users/:id", (req: Request, res: Response) => {
  const userId = parseInt(req.params.id);

  if (isNaN(userId)) {
    return res.status(400).json({ error: "Invalid user ID" });
  }

  const user = users.find((u) => u.id === userId);

  if (!user) {
    return res.status(404).json({ error: "User not found" });
  }

  return res.json(user);
});

// Error handling middleware
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: "Something went wrong" });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Conclusion

TypeScript represents a significant evolution in the JavaScript ecosystem, bringing the benefits of static typing to a dynamically typed language. For developers with experience in HTML, CSS, JavaScript, and PHP, TypeScript offers a natural progression that enhances productivity and code quality.

In the following modules, you'll explore TypeScript in greater depth, from basic types to advanced features, and learn how to apply TypeScript effectively in real-world projects.

By mastering TypeScript, you'll gain skills that are highly valued in the industry and improve your ability to build robust, maintainable web applications.