Module 1.3: TypeScript Crash Course

🎯 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
Productobject.
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?
| Benefit | Description |
|---|---|
| Early Error Detection | Type errors are flagged during development, not at runtime. |
| Improved IntelliSense | Your IDE understands your code better, providing accurate autocompletion. |
| Safer Refactoring | Rename and restructure your code confidently, without fear of breaking things. |
| Self-Documenting Code | Types serve as living documentation for your codebase. |
1.2. Installing TypeScript
Check your installation:
npx tsc --version
# Expected: Version 5.x.xInstall globally (optional):
npm install -g typescript2. Type Inference vs Explicit Types
2.1. Type Inference
TypeScript automatically infers types based on assigned values.

// 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?
| Scenario | Recommended | Reason |
|---|---|---|
| Variables initialized with a value | Inference | TypeScript understands it automatically |
| Function parameters | Explicit | TypeScript cannot infer them |
| Function return (complex) | Explicit | Ensures intended type |
| Uninitialized variables | Explicit | Nothing to infer from |
| API Response | Explicit | External 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 safety3.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-only3.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'; // Error4. 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

| Feature | Interface | Type |
|---|---|---|
| Object shape | ✅ | ✅ |
| Extend/Inherit | extends | & (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: string5.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' property5.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.tsLast updated on