Published on

TypeScript for JavaScript Developers - The Complete Beginner's Guide

Authors

Introduction

TypeScript is no longer optional — it's the professional standard for JavaScript development in 2026. It adds static types to JavaScript, catches bugs at compile time, and makes your IDE incredibly powerful with intelligent autocomplete.

If you know JavaScript, this guide will get you up to speed with TypeScript in one sitting.

What is TypeScript?

TypeScript is a superset of JavaScript — all valid JavaScript is valid TypeScript. TypeScript adds type annotations that are checked at compile time, then stripped out when compiled to JavaScript.

JavaScript code → TypeScript adds types → TypeScript compiler checks types → Outputs clean JavaScript

Setup

npm install -g typescript
tsc --init  # Creates tsconfig.json

# Or use ts-node for direct execution
npm install -g ts-node
ts-node your-file.ts

Type Annotations

// Variables
let name: string = 'Alice'
let age: number = 30
let isActive: boolean = true
let anything: any = 'can be anything'  // avoid this!

// Functions
function greet(name: string): string {
  return `Hello, ${name}!`
}

// Arrow functions
const add = (a: number, b: number): number => a + b

Arrays and Objects

// Arrays
const numbers: number[] = [1, 2, 3]
const names: string[] = ['Alice', 'Bob']
const mixed: (string | number)[] = ['hello', 42]

// Objects — using interfaces
interface User {
  id: number
  name: string
  email: string
  age?: number  // optional property
}

const user: User = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  // age is optional — OK to omit
}

Interfaces vs Types

Both define shapes, but there are differences:

// Interface — preferred for object shapes
interface Animal {
  name: string
  sound(): string
}

// Type — great for unions and complex types
type Status = 'active' | 'inactive' | 'pending'
type ID = string | number

// Extending interfaces
interface Dog extends Animal {
  breed: string
}

// Intersection types
type AdminUser = User & { adminLevel: number }

Union and Intersection Types

// Union — can be one OR the other
type StringOrNumber = string | number

function formatId(id: string | number): string {
  return typeof id === 'string' ? id.toUpperCase() : id.toString()
}

// Literal types
type Direction = 'north' | 'south' | 'east' | 'west'
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6

function move(direction: Direction) {
  console.log(`Moving ${direction}`)
}
move('north')  // ✅
move('up')     // ❌ TypeScript error!

Generics

Generics make components reusable with different types:

// Without generics — not type-safe
function getFirst(arr: any[]): any {
  return arr[0]
}

// With generics — type-safe ✅
function getFirst<T>(arr: T[]): T {
  return arr[0]
}

const firstNum = getFirst([1, 2, 3])    // Type: number
const firstName = getFirst(['a', 'b'])  // Type: string

// Generic interfaces
interface ApiResponse<T> {
  data: T
  status: number
  message: string
}

const userResponse: ApiResponse<User> = {
  data: { id: 1, name: 'Alice', email: 'a@b.com' },
  status: 200,
  message: 'Success',
}

Enums

enum UserRole {
  ADMIN = 'admin',
  USER = 'user',
  MODERATOR = 'moderator',
}

function checkPermissions(role: UserRole) {
  if (role === UserRole.ADMIN) {
    console.log('Full access')
  }
}

checkPermissions(UserRole.ADMIN)  // ✅
checkPermissions('admin')         // ❌ Error!

Type Narrowing

TypeScript is smart about types in conditionals:

function processInput(input: string | number) {
  if (typeof input === 'string') {
    // TypeScript knows input is string here
    return input.toUpperCase()
  } else {
    // TypeScript knows input is number here
    return input * 2
  }
}

// Nullish checks
function getLength(str: string | null | undefined): number {
  if (str == null) return 0
  return str.length  // TypeScript knows str is string here
}

Utility Types

TypeScript ships with powerful built-in utility types:

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

// Partial — all properties optional
type PartialUser = Partial<User>

// Required — all properties required
type RequiredUser = Required<User>

// Pick — select specific properties
type PublicUser = Pick<User, 'id' | 'name' | 'email'>

// Omit — exclude specific properties
type SafeUser = Omit<User, 'password'>

// Record — dictionary type
type UserMap = Record<string, User>

// Readonly — immutable
type ImmutableUser = Readonly<User>

Real-World Example

interface Product {
  id: number
  name: string
  price: number
  category: string
  inStock: boolean
}

type CreateProductInput = Omit<Product, 'id'>
type UpdateProductInput = Partial<Omit<Product, 'id'>>

class ProductService {
  private products: Product[] = []

  create(input: CreateProductInput): Product {
    const product: Product = {
      id: Date.now(),
      ...input,
    }
    this.products.push(product)
    return product
  }

  update(id: number, changes: UpdateProductInput): Product | null {
    const idx = this.products.findIndex(p => p.id === id)
    if (idx === -1) return null
    this.products[idx] = { ...this.products[idx], ...changes }
    return this.products[idx]
  }

  getByCategory(category: string): Product[] {
    return this.products.filter(p => p.category === category)
  }
}

Conclusion

TypeScript makes JavaScript dramatically safer and more maintainable. The initial setup cost pays back tenfold in fewer bugs, better refactoring, and a far superior IDE experience. Start by adding TypeScript to your next project — you'll never want to go back.