Getting Started with TypeScript: A Practical Guide

TypeScript has become the standard for building large-scale JavaScript applications. In this comprehensive guide, we'll explore everything you need to know to get started with TypeScript and write better, more maintainable code.
Why TypeScript?#
TypeScript adds static type checking to JavaScript, catching errors before they reach production. But it's more than just types—it's a better developer experience.
TypeScript is JavaScript that scales. It's a strict syntactical superset of JavaScript that adds optional static typing.
— Anders Hejlsberg, TypeScript Creator
Setting Up Your Environment#
First, let's install TypeScript globally:
npm install -g typescript
Create a new project and initialize it:
mkdir my-ts-project
cd my-ts-project
npm init -y
npm install typescript --save-dev
npx tsc --init
This generates a tsconfig.json file with sensible defaults.
Basic Types#
TypeScript provides several built-in types. Let's explore the most common ones:
// Primitive types
const name: string = "Alice";
const age: number = 30;
const isActive: boolean = true;
// Arrays
const numbers: number[] = [1, 2, 3, 4, 5];
const names: Array<string> = ["Alice", "Bob", "Charlie"];
// Tuples - fixed length arrays with specific types
const coordinate: [number, number] = [10, 20];
const record: [string, number, boolean] = ["Alice", 30, true];
Interfaces and Type Aliases#
Interfaces define the shape of objects:
interface User {
id: number;
name: string;
email: string;
age?: number; // Optional property
readonly createdAt: Date; // Cannot be modified after creation
}
// Using the interface
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
createdAt: new Date()
};
Type aliases offer similar functionality with some differences:
type Point = {
x: number;
y: number;
};
// Union types - only possible with type aliases
type Status = "pending" | "approved" | "rejected";
// Intersection types
type Employee = User & {
department: string;
salary: number;
};
Functions with Types#
TypeScript brings type safety to functions:
// Function with typed parameters and return type
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
// Arrow function with types
const add = (a: number, b: number): number => a + b;
// Function with optional parameters
function createUser(name: string, age?: number): User {
return {
id: Math.random(),
name,
email: `${name.toLowerCase()}@example.com`,
age,
createdAt: new Date()
};
}
// Rest parameters
function sum(...numbers: number[]): number {
return numbers.reduce((acc, curr) => acc + curr, 0);
}
Generics#
Generics allow you to write reusable, type-safe code:
// Generic function
function identity<T>(value: T): T {
return value;
}
const str = identity<string>("hello"); // type: string
const num = identity(42); // type: number (inferred)
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
// Using generic interface
interface Product {
id: number;
name: string;
price: number;
}
const response: ApiResponse<Product[]> = {
data: [{ id: 1, name: "Widget", price: 9.99 }],
status: 200,
message: "Success"
};
// Generic with constraint
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): void {
console.log(`Length: ${item.length}`);
}
logLength("hello"); // Works - strings have length
logLength([1, 2, 3]); // Works - arrays have length
// logLength(42); // Error - numbers don't have length
Working with Classes#
TypeScript enhances JavaScript classes with access modifiers and more:
class BankAccount {
// Properties with access modifiers
private balance: number;
public readonly accountNumber: string;
protected owner: string;
constructor(owner: string, initialBalance: number = 0) {
this.accountNumber = this.generateAccountNumber();
this.owner = owner;
this.balance = initialBalance;
}
private generateAccountNumber(): string {
return Math.random().toString(36).substring(2, 15);
}
public deposit(amount: number): void {
if (amount <= 0) {
throw new Error("Deposit amount must be positive");
}
this.balance += amount;
}
public withdraw(amount: number): boolean {
if (amount > this.balance) {
return false;
}
this.balance -= amount;
return true;
}
public getBalance(): number {
return this.balance;
}
}
Utility Types#
TypeScript provides built-in utility types for common transformations:
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: Date;
}
// Partial - makes all properties optional
type PartialTodo = Partial<Todo>;
// Required - makes all properties required
type RequiredTodo = Required<Todo>;
// Pick - select specific properties
type TodoPreview = Pick<Todo, "title" | "completed">;
// Omit - exclude specific properties
type TodoWithoutDates = Omit<Todo, "createdAt">;
// Readonly - makes all properties readonly
type ReadonlyTodo = Readonly<Todo>;
// Record - create an object type with specific keys
type TodoRecord = Record<string, Todo>;
Error Handling with Types#
Type-safe error handling patterns:
// Result type pattern
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
async function fetchUser(id: number): Promise<Result<User>> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return {
success: false,
error: new Error(`HTTP ${response.status}`)
};
}
const data = await response.json();
return { success: true, data };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error : new Error(String(error))
};
}
}
// Usage with type narrowing
const result = await fetchUser(1);
if (result.success) {
console.log(result.data.name); // TypeScript knows data is User
} else {
console.error(result.error.message); // TypeScript knows error is Error
}
Next Steps#
Now that you understand the basics, here are some resources to continue your TypeScript journey:
- TypeScript Handbook - Official documentation
- TypeScript Playground - Experiment with TypeScript online
- DefinitelyTyped - Type definitions for JavaScript libraries
TypeScript transforms how you write JavaScript. Start small, gradually add types to your existing projects, and watch your code quality improve dramatically.



