TypeScript - Type-Safe JavaScript for Modern Web Development

TypeScript is a statically typed superset of JavaScript developed by Microsoft. It compiles to plain JavaScript and adds optional type annotations that catch errors at compile time.

TypeScript improves code quality, enables better tooling with IDE autocompletion, and makes large codebases easier to maintain and refactor.

This tutorial introduces TypeScript fundamentals, including types, interfaces, generics, and how to integrate TypeScript with popular frameworks.

Basic Types in TypeScript

TypeScript adds type annotations to JavaScript variables, function parameters, and return values to catch type errors before runtime.

TypeScript
Basic TypeScript type annotations
// Primitive types
let name: string = 'Alice';
let age: number = 25;
let isActive: boolean = true;

// Arrays
let scores: number[] = [90, 85, 92];
let tags: Array<string> = ['javascript', 'typescript'];

// Union types
let id: string | number = 'user-123';
id = 456; // also valid

// Tuple
let coordinates: [number, number] = [13.08, 80.27];

// Any and unknown
let data: unknown = fetchData();
if (typeof data === 'string') {
  console.log(data.toUpperCase());
}

Interfaces and Type Aliases

Interfaces define the shape of an object in TypeScript. Type aliases are similar but also work for primitives, unions, and complex types.

TypeScript
Interfaces and type aliases with optional properties
// Interface
interface User {
  id: number;
  name: string;
  email: string;
  role?: 'admin' | 'user'; // optional property
  readonly createdAt: Date; // cannot be changed after creation
}

// Extending interfaces
interface AdminUser extends User {
  permissions: string[];
}

// Type alias for complex types
type ApiResponse<T> = {
  data: T;
  status: number;
  message: string;
};

const response: ApiResponse<User[]> = {
  data: [],
  status: 200,
  message: 'Success'
};

Generics in TypeScript

Generics allow you to write reusable, type-safe functions and classes that work with any data type while preserving type information.

TypeScript
Generic functions and generic React components
// Generic function
function getFirst<T>(arr: T[]): T | undefined {
  return arr[0];
}

const firstNum = getFirst([1, 2, 3]);     // TypeScript infers: number
const firstStr = getFirst(['a', 'b']);    // TypeScript infers: string

// Generic interface
interface Repository<T> {
  findById(id: number): Promise<T>;
  findAll(): Promise<T[]>;
  save(entity: T): Promise<T>;
}

// Generic React component prop
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

Type Narrowing and Type Guards

Type narrowing allows TypeScript to automatically infer a more specific type based on runtime checks like typeof, instanceof, and in operators.

TypeScript
Type narrowing with typeof and custom type guards
type StringOrNumber = string | number;

function processInput(input: StringOrNumber) {
  if (typeof input === 'string') {
    return input.toUpperCase(); // TypeScript knows it's a string here
  }
  return input.toFixed(2); // TypeScript knows it's a number here
}

// Custom type guard
interface Cat { meow(): void; }
interface Dog { bark(): void; }

function isCat(pet: Cat | Dog): pet is Cat {
  return (pet as Cat).meow !== undefined;
}

Why Use TypeScript?

  • Catches type-related bugs at compile time, not runtime
  • Provides rich IDE autocompletion, IntelliSense, and refactoring tools
  • Makes code self-documenting through explicit type annotations
  • Scales well for large teams and codebases
  • Gradually adoptable — plain JavaScript is valid TypeScript
  • Used by major frameworks: Angular (required), React (recommended), Vue, Next.js

Conclusion

TypeScript has become the standard for large-scale JavaScript development. It brings type safety, better tooling, and improved code quality to any JavaScript project.

Start by adding TypeScript to a small project, learn the fundamentals, and gradually adopt advanced features like generics and utility types to get the most out of it.