TypeScript - Modern Type-Safe JavaScript Explained

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript, catching errors at compile-time and improving developer experience.

From interfaces and generics to advanced utility types, TypeScript scales from small scripts to large enterprise applications.

This tutorial covers essential TypeScript features with practical examples for building robust, maintainable applications.

Basic Types and Interfaces

TypeScript provides primitive types, object types via interfaces, and type aliases for reusable type definitions.

TypeScript
type ID = string | number;

interface User {
  id: ID;
  name: string;
  email: string;
  isActive: boolean;
  role?: string; // Optional
}

const user: User = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  isActive: true
};

// Readonly interface
interface Point {
  readonly x: number;
  readonly y: number;
}

const point: Point = { x: 10, y: 20 };
// point.x = 5; // Error!

Use interfaces for object shapes and type aliases for primitives, unions, and complex types.

Union and Intersection Types

Union types (|) allow a value to be one of several types. Intersection types (&) combine multiple types into one.

TypeScript
// Union types
type Status = 'loading' | 'success' | 'error';

function setStatus(status: Status) {
  // TypeScript knows possible values
}

// Intersection types
type Admin = User & { permissions: string[] };
const admin: Admin = {
  ...user,
  permissions: ['read', 'write', 'delete']
};

// Discriminated unions
type ApiResponse<T> = 
  | { success: true; data: T }
  | { success: false; error: string };

Generics

Generics create reusable components that work with multiple types while maintaining type safety.

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

const num = identity<number>(42);
const str = identity('hello');

// Generic class
class Box<T> {
  private value: T;
  
  constructor(value: T) {
    this.value = value;
  }
  
  getValue(): T {
    return this.value;
  }
}

const numberBox = new Box<number>(100);

// Generic constraints
function getLength<T extends { length: number }>(item: T): number {
  return item.length;
}

getLength('hello'); // 5
g// getLength(42); // Error!

Utility Types

TypeScript provides built-in utility types for common type transformations.

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

// Pick and Omit
type UserInfo = Pick<User, 'id' | 'name' | 'email'>;
type UserCredentials = Pick<User, 'email' | 'password'>;

type PublicUser = Omit<User, 'password'>;

// Partial and Required
type UpdateUser = Partial<User>;
const update: UpdateUser = { name: 'Bob' };

type RequiredUser = Required<User>;

// Record utility
type UserDict = Record<string, User>;
const users: UserDict = {
  '1': user1,
  '2': user2
};

Advanced Patterns

TypeScript
// Mapped types
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

type Partial<T> = {
  [K in keyof T]?: T[K];
};

// Conditional types
type NonNullable<T> = T extends null | undefined ? never : T;

type Extract<T, U> = T extends U ? T : never;

type UserId = Extract<User['id'], string | number>; // number

// Template literal types (TS 4.1+)
type Event = `on${'Click' | 'Submit' | 'Change'}`;
const event: Event = 'onClick';

React + TypeScript

TypeScript supercharges React development with typed props, state, and hooks.

TSX
interface ButtonProps {
  variant?: 'primary' | 'secondary';
  onClick: () => void;
  children: React.ReactNode;
  disabled?: boolean;
}

function Button({ variant = 'primary', onClick, children, disabled }: ButtonProps) {
  return (
    <button
      className={`btn btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
}

// Generic React component
function List<T>({ items, renderItem }: {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{renderItem(item)}</li>
      ))}
    </ul>
  );
}

tsconfig.json Essentials

  • "strict": true - Enables all strict type-checking
  • "noImplicitAny": true - Prevents implicit any types
  • "strictNullChecks": true - Distinguishes null/undefined
  • "esModuleInterop": true - Better module compatibility
  • "skipLibCheck": true - Skip type checking of declaration files
  • "jsx": "react-jsx" - Modern React JSX transform
JSON
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "jsx": "react-jsx"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Conclusion

TypeScript transforms JavaScript development by catching errors early, improving code quality, and enabling better IDE support.

Start with basic types and interfaces, then progress to generics, utility types, and advanced patterns for enterprise-scale applications.

Integrate TypeScript gradually into existing projects and gradually increase strictness for maximum benefits.