🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

TypeScript Complete Guide 2026: Types, Generics and Production Patterns

⏱️7 min read  ·  1,362 words

TypeScript has become the standard for modern JavaScript development. In 2026, over 80% of new JavaScript projects use TypeScript. This complete guide covers everything from basic types to advanced generics, decorators, and production patterns.

Why TypeScript in 2026?

JavaScript’s dynamic nature causes runtime errors that TypeScript catches at compile time. The benefits are clear:

  • Catch bugs early — type errors at compile time, not in production
  • Better tooling — autocomplete, refactoring, and navigation in VS Code
  • Self-documenting code — types serve as inline documentation
  • Large-scale confidence — safe refactoring across thousands of files

Installation and Setup

Install TypeScript globally or as a dev dependency:

# Install TypeScript
npm install -g typescript

# Or as dev dependency
npm install --save-dev typescript

# Check version
tsc --version

# Initialize tsconfig
tsc --init

tsconfig.json Best Practices

A production-ready tsconfig.json for 2026:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "lib": ["ES2022", "DOM"],
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "sourceMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Basic Types

TypeScript’s type system starts with primitives and builds up:

// Primitives
const name: string = "Alice";
const age: number = 30;
const active: boolean = true;
const nothing: null = null;
const unknown: undefined = undefined;

// Arrays
const scores: number[] = [95, 87, 92];
const tags: Array<string> = ["ts", "js", "node"];

// Tuples (fixed-length arrays with known types)
const point: [number, number] = [10, 20];
const entry: [string, number] = ["age", 30];

// Union types
let id: string | number = "user_123";
id = 456; // also valid

// Literal types
type Direction = "north" | "south" | "east" | "west";
const dir: Direction = "north";

// any, unknown, never
const userInput: unknown = getUserInput();
if (typeof userInput === "string") {
  console.log(userInput.toUpperCase()); // type narrowed to string
}

Interfaces vs Types

Both define object shapes, but with key differences:

// Interface — can be merged (declaration merging)
interface User {
  id: number;
  name: string;
  email?: string; // optional
}

// Extend interface
interface AdminUser extends User {
  role: "admin" | "superadmin";
  permissions: string[];
}

// Type alias — more flexible, supports unions and intersections
type ApiResponse<T> = {
  data: T;
  error: string | null;
  status: number;
};

// Intersection type
type AuthUser = User & { token: string };

// Prefer interface for object shapes, type for unions/intersections
const user: AdminUser = {
  id: 1,
  name: "Alice",
  role: "admin",
  permissions: ["read", "write"]
};

Generics

Generics make code reusable while staying type-safe:

// Generic function
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

const num = first([1, 2, 3]); // type: number | undefined
const str = first(["a", "b"]); // type: string | undefined

// Generic with constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: "Alice", age: 30 };
const name = getProperty(user, "name"); // type: string
const id = getProperty(user, "id");     // type: number

// Generic interface
interface Repository<T> {
  findById(id: number): Promise<T | null>;
  findAll(): Promise<T[]>;
  create(item: Omit<T, "id">): Promise<T>;
  update(id: number, item: Partial<T>): Promise<T>;
  delete(id: number): Promise<void>;
}

// Generic class
class Stack<T> {
  private items: T[] = [];

  push(item: T): void { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
  peek(): T | undefined { return this.items[this.items.length - 1]; }
  isEmpty(): boolean { return this.items.length === 0; }
}

const stack = new Stack<number>();
stack.push(1);
stack.push(2);
console.log(stack.pop()); // 2

Utility Types

TypeScript ships with powerful built-in utility types:

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

// Partial — all fields optional
type ProductDraft = Partial<Product>;

// Required — all fields required
type FullProduct = Required<Product>;

// Pick — select specific fields
type ProductSummary = Pick<Product, "id" | "name" | "price">;

// Omit — exclude fields
type NewProduct = Omit<Product, "id">;

// Readonly — prevent mutation
type ImmutableProduct = Readonly<Product>;

// Record — map type
type CategoryMap = Record<string, Product[]>;

// ReturnType — extract function return type
async function fetchUser() {
  return { id: 1, name: "Alice", email: "alice@example.com" };
}
type User = Awaited<ReturnType<typeof fetchUser>>;

// Parameters — extract function parameters
function createOrder(userId: number, items: string[], total: number) {}
type OrderParams = Parameters<typeof createOrder>;

Advanced Patterns: Conditional Types

// Conditional type
type IsArray<T> = T extends any[] ? true : false;

type A = IsArray<string[]>; // true
type B = IsArray<string>;   // false

// Infer keyword
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type Unpacked = UnpackArray<string[]>; // string
type NotArray = UnpackArray<number>;   // number

// Mapped types
type Optional<T> = {
  [K in keyof T]?: T[K];
};

// Template literal types (TypeScript 4.1+)
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type FocusEvent = EventName<"focus">; // "onFocus"

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

function processResult<T>(result: Result<T>): T {
  if (result.success) {
    return result.data; // TypeScript knows this is T
  }
  throw new Error(result.error); // TypeScript knows error is string
}

TypeScript with React

import React, { useState, useCallback, FC } from "react";

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary" | "danger";
  disabled?: boolean;
  children?: React.ReactNode;
}

const Button: FC<ButtonProps> = ({
  label,
  onClick,
  variant = "primary",
  disabled = false,
  children
}) => {
  return (
    <button
      className={`btn btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children ?? label}
    </button>
  );
};

// Generic component
interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string | number;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={keyExtractor(item)}>{renderItem(item, index)}</li>
      ))}
    </ul>
  );
}

// Custom hook with types
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  const setStoredValue = useCallback((newValue: T) => {
    setValue(newValue);
    localStorage.setItem(key, JSON.stringify(newValue));
  }, [key]);

  return [value, setStoredValue] as const;
}

TypeScript Decorators (2026)

TypeScript 5.0+ supports Stage 3 decorators:

// Class decorator
function singleton<T extends { new(...args: any[]): {} }>(constructor: T) {
  let instance: T;
  return class extends constructor {
    constructor(...args: any[]) {
      if (instance) return instance;
      super(...args);
      instance = this as any;
    }
  };
}

// Method decorator — log execution time
function measure(_target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = async function (...args: any[]) {
    const start = performance.now();
    const result = await original.apply(this, args);
    const duration = performance.now() - start;
    console.log(`${key} took ${duration.toFixed(2)}ms`);
    return result;
  };
  return descriptor;
}

class UserService {
  @measure
  async fetchUsers(): Promise<User[]> {
    const r = await fetch("/api/users");
    return r.json();
  }
}

Strict Mode Checklist

Enable these compiler options for maximum type safety:

  • strict: true — enables all strict checks
  • noUncheckedIndexedAccess: true — array access returns T | undefined
  • exactOptionalPropertyTypes: trueundefined not assignable to optional
  • noImplicitOverride: true — must use override keyword
  • noPropertyAccessFromIndexSignature: true — use bracket notation for index sigs

TypeScript Tooling in 2026

  • Biome — fastest linter + formatter, TypeScript-native
  • tsx — run TypeScript files directly without compiling
  • tsup — zero-config bundler for TypeScript libraries
  • Vitest — TypeScript-first testing with native ESM
  • tRPC — end-to-end type safety for full-stack apps

Key Takeaways

TypeScript in 2026 is mature, fast, and essential for professional JavaScript development. Start with strict: true, use interfaces for object shapes, lean on utility types, and leverage generics for reusable code. The tooling ecosystem has never been better.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা