Skip to Content
Module 1: Modern JS & TypeScript1.3 TypeScript Crash Course

Module 1.3: TypeScript Crash Course

TypeScript Crash Course Banner Image

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Understand the difference between Type Inference and Explicit Types.
  • Distinguish between an Interface and a Type, and know when to use each.
  • Apply basic Generics for API Response typing.
  • Complete a Lab: Define Types for a complex Product object.

1. What is TypeScript?

TypeScript is a superset of JavaScript that adds a static type system. TypeScript code compiles down to JavaScript before it runs.

1.1. Why Use TypeScript with React?

BenefitDescription
Early Error DetectionType errors are flagged during development, not at runtime.
Improved IntelliSenseYour IDE understands your code better, providing accurate autocompletion.
Safer RefactoringRename and restructure your code confidently, without fear of breaking things.
Self-Documenting CodeTypes serve as living documentation for your codebase.

1.2. Installing TypeScript

Check your installation:

npx tsc --version # Expected: Version 5.x.x

Install globally (optional):

npm install -g typescript

2. Type Inference vs Explicit Types

2.1. Type Inference

TypeScript automatically infers types based on assigned values.

Example of TypeScript's Type Inference in action

// TypeScript AUTOMATICALLY infers types let message = 'Hello'; // type: string let count = 42; // type: number let isActive = true; // type: boolean let items = [1, 2, 3]; // type: number[] // Hover in your IDE to see the type const user = { name: 'Alice', age: 25, }; // type: { name: string; age: number } // Function return types are also inferred const add = (a: number, b: number) => a + b; // return type: number (inferred from a + b)

2.2. Explicit Types

Explicitly declare types.

// Explicit declaration let message: string = 'Hello'; let count: number = 42; let isActive: boolean = true; let items: number[] = [1, 2, 3]; // Object with type annotation let user: { name: string; age: number } = { name: 'Alice', age: 25, }; // Function with explicit types const add = (a: number, b: number): number => { return a + b; };

2.3. When to Use Which?

ScenarioRecommendedReason
Variables initialized with a valueInferenceTypeScript understands it automatically
Function parametersExplicitTypeScript cannot infer them
Function return (complex)ExplicitEnsures intended type
Uninitialized variablesExplicitNothing to infer from
API ResponseExplicitExternal data
// ✅ Let TypeScript infer const name = 'Alice'; // Inference: string const numbers = [1, 2, 3]; // Inference: number[] // ✅ Use Explicit when necessary let userId: number; // No initial value function greet(name: string): string { // Parameters + Return return `Hello, ${name}`; } // ✅ Explicit for external data const response: ApiResponse = await fetch('/api/users');

3. Basic Types

3.1. Primitive Types

// String let firstName: string = 'John'; // Number (both integers and floats) let age: number = 25; let price: number = 99.99; // Boolean let isLoggedIn: boolean = false; // Null and Undefined let nothing: null = null; let notDefined: undefined = undefined; // Any (use sparingly!) let anything: any = 'hello'; anything = 42; // No error, but loses type safety

3.2. Arrays and Tuples

// Array let numbers: number[] = [1, 2, 3]; let names: Array<string> = ['Alice', 'Bob']; // Generic syntax // Mixed array let mixed: (string | number)[] = [1, 'hello', 2]; // Tuple (fixed length, fixed types) let coordinate: [number, number] = [10, 20]; let userTuple: [string, number, boolean] = ['Alice', 25, true]; // Tuples with labels (TS 4.0+) let person: [name: string, age: number] = ['Alice', 25];

3.3. Object Types

// Inline object type let user: { name: string; age: number } = { name: 'Alice', age: 25, }; // Optional properties using ? let config: { theme: string; debug?: boolean } = { theme: 'dark', // debug is optional, no need to declare it }; // Readonly properties let point: { readonly x: number; readonly y: number } = { x: 10, y: 20, }; // point.x = 30; // Error: Cannot assign to 'x' because it is read-only

3.4. Union Types

// Union: can be one of several types let id: string | number; id = 'abc123'; // OK id = 123; // OK // id = true; // Error // Commonly used for function parameters function printId(id: string | number) { console.log(`ID: ${id}`); } // Literal types let status: 'loading' | 'success' | 'error'; status = 'loading'; // OK // status = 'pending'; // Error

4. Interface vs Type

4.1. Interface

An interface defines the structure of an object.

// Define an interface interface User { id: number; name: string; email: string; age?: number; // Optional } // Usage const user: User = { id: 1, name: 'Alice', email: 'alice@example.com', }; // Extending interface interface Employee extends User { department: string; salary: number; } const employee: Employee = { id: 1, name: 'Bob', email: 'bob@company.com', department: 'Engineering', salary: 50000, };

4.2. Type Alias

A type can define any type, not just objects.

// Type for an object (similar to an interface) type User = { name: string; email: string; }; // Type for primitives type ID = string | number; type Status = 'pending' | 'approved' | 'rejected';
// Type for a function type GreetFunction = (name: string) => string; // Type for a tuple type Coordinate = [number, number]; // Intersection types (combining types) type Employee = User & { department: string; salary: number; };

4.3. Interface vs. Type: A Comparison

Comparison table for Interface vs Type features

FeatureInterfaceType
Object shape
Extend/Inheritextends& (intersection)
Union types
Primitives
Tuples
Declaration merging

4.4. When to Use Which?

// ✅ Use INTERFACE for: // - Object shapes (especially for React props/state) // - When you need to extend/implement // - API contracts interface UserProps { name: string; onSelect: (id: number) => void; } // ✅ Use TYPE for: // - Union types // - Primitives // - Tuples // - Function types // - Complex type transformations type Status = 'loading' | 'success' | 'error'; type ID = string | number; type Handler = (event: Event) => void;

Practical Rule of Thumb: In React, use interface for Props and State, and type for everything else.

5. Generics

5.1. What Are Generics?

Generics allow you to build components or functions that can work with a variety of types.

// ❌ Without generics - requires multiple functions function getFirstNumber(arr: number[]): number { return arr[0]; } function getFirstString(arr: string[]): string { return arr[0]; } // ✅ With Generics - one function for all types function getFirst<T>(arr: T[]): T { return arr[0]; } // Usage const firstNum = getFirst([1, 2, 3]); // type: number const firstStr = getFirst(['a', 'b', 'c']); // type: string

5.2. Generics with Interfaces

// Generic interface interface ApiResponse<T> { message: string; data: T; } // Using with different types interface User { id: number; name: string; } interface Product { id: number; title: string; price: number; } // Response for User const userResponse: ApiResponse<User> = { message: 'Success', data: { id: 1, name: 'Alice' }, };
// Response for Product[] const productsResponse: ApiResponse<Product[]> = { message: 'Success', data: [ { id: 1, title: 'iPhone', price: 999 }, { id: 2, title: 'MacBook', price: 1999 }, ], };

5.3. Generic Constraints

// Constrain T to have an 'id' property interface HasId { id: number; } function findById<T extends HasId>(items: T[], id: number): T | undefined { return items.find((item) => item.id === id); } // Usage const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, ]; const user = findById(users, 1); // OK // const result = findById(['a', 'b'], 1); // Error: string does not have an 'id' property

5.4. Real-world Application: API Service

// Generic API Response interface ApiResponse<T> { success: boolean; data: T; message?: string; } // Generic fetch function async function fetchApi<T>(url: string): Promise<ApiResponse<T>> { const response = await fetch(url); const json = await response.json(); return json as ApiResponse<T>; }
// Usage interface User { id: number; name: string; email: string; } const result = await fetchApi<User[]>('/api/users'); // result.data has type: User[] if (result.success) { result.data.forEach((user) => { console.log(user.name); // TypeScript knows 'user' has a 'name' property }); }

6. Lab: Define Types for a Product

6.1. Requirements

Create types for an E-commerce Product with nested objects.

6.2. Type Definitions

// product-types.ts // Type aliases export type ProductStatus = 'draft' | 'active' | 'archived'; // Category with nested parent export interface Category { id: number; name: string; parent?: Category; } export interface Variant { id: number; color: string; size: string; stock: number; price: number; } export interface Product { id: number; name: string; description: string; price: number; status: ProductStatus; category: Category; variants: Variant[]; tags: string[]; createdAt: string; } // Generic API Response export interface ApiResponse<T> { success: boolean; data: T; error?: string; } // Utility Types export type CreateProduct = Omit<Product, 'id' | 'createdAt'>; export type UpdateProduct = Partial<Product> & { id: number };

6.3. Using the Types

// example.ts import { Product, Category, ApiResponse } from './product-types'; // Create a nested Category const category: Category = { id: 2, name: 'Phones', parent: { id: 1, name: 'Electronics' }, }; // Create a Product const product: Product = { id: 1, name: 'iPhone 15', description: 'Latest iPhone', price: 999, status: 'active', category: category, variants: [ { id: 1, color: 'Black', size: '128GB', stock: 50, price: 999 }, { id: 2, color: 'White', size: '256GB', stock: 30, price: 1099 }, ], tags: ['phone', 'apple'], createdAt: '2026-01-01', }; // API Response with Generics const response: ApiResponse<Product> = { success: true, data: product, }; // Type-safe access if (response.success) { console.log(response.data.name); console.log(response.data.category.parent?.name); response.data.variants.forEach((v) => { console.log(`${v.color} - ${v.size}: $${v.price}`); }); }

6.4. Running the Code

npx ts-node example.ts
Last updated on