+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 93 of 355

🌊 Control Flow Analysis: How TypeScript Tracks Types

Explore TypeScript's control flow analysis engine to understand how the compiler tracks type changes, narrows types through code paths, and provides intelligent type inference 🚀

💎Advanced
28 min read

Prerequisites

  • Deep understanding of union types and type guards 📝
  • Experience with type predicates and assertion functions ⚡
  • Knowledge of TypeScript's type narrowing mechanisms 💻

What you'll learn

  • Understand how TypeScript's control flow analysis works internally 🎯
  • Master advanced type narrowing and flow-sensitive typing 🏗️
  • Leverage control flow analysis for better type safety 🐛
  • Optimize code structure for better type inference ✨

🎯 Introduction

Welcome to the analytical heart of TypeScript! 🌊 This tutorial explores the sophisticated control flow analysis engine that powers TypeScript’s intelligent type narrowing and tracking. Understanding how TypeScript traces types through your code paths is crucial for writing type-safe code that fully leverages the compiler’s capabilities.

You’ll discover how TypeScript analyzes branches, loops, function calls, and complex control structures to track type changes throughout your program. Whether you’re building complex conditional logic 🧠, handling error cases 🛡️, or working with discriminated unions 🔀, understanding control flow analysis will help you write more expressive and type-safe code.

By the end of this tutorial, you’ll understand the inner workings of TypeScript’s type tracking and be able to write code that works harmoniously with the compiler’s analysis! Let’s flow with the types! 💫

📚 Understanding Control Flow Analysis

🤔 What Is Control Flow Analysis?

Control flow analysis is TypeScript’s mechanism for tracking how types change as code execution flows through different branches, conditions, and statements. It enables type narrowing and provides the foundation for TypeScript’s intelligent type inference:

// 🌟 Basic control flow analysis
function processValue(input: string | number | null) {
  // 🎯 Initial type: string | number | null
  console.log('Input type:', typeof input);
  
  if (input === null) {
    // ✨ TypeScript narrows to: null
    console.log('Input is null');
    return;
  }
  
  // ✨ TypeScript narrows to: string | number
  if (typeof input === 'string') {
    // ✨ TypeScript narrows to: string
    console.log('String length:', input.length);
    console.log('Uppercase:', input.toUpperCase());
    return;
  }
  
  // ✨ TypeScript narrows to: number
  console.log('Number value:', input.toFixed(2));
  console.log('Is integer:', Number.isInteger(input));
}

// 🔍 Control flow with early returns
function validateUser(user: unknown): string {
  if (typeof user !== 'object' || user === null) {
    throw new Error('Expected object');
  }
  
  // ✨ TypeScript knows user is object (not null)
  if (!('name' in user)) {
    throw new Error('Missing name property');
  }
  
  // ✨ TypeScript knows user has 'name' property
  const name = (user as any).name;
  
  if (typeof name !== 'string') {
    throw new Error('Name must be string');
  }
  
  // ✨ TypeScript knows name is string
  return name.trim();
}

// 🌟 Flow analysis with complex conditions
function analyzeData(data: string | number | boolean | null | undefined) {
  // Initial type: string | number | boolean | null | undefined
  
  if (data == null) {
    // ✨ Narrows to: null | undefined
    console.log('Data is nullish');
    return;
  }
  
  // ✨ Narrows to: string | number | boolean
  if (typeof data === 'boolean') {
    // ✨ Narrows to: boolean
    console.log('Boolean value:', data ? 'true' : 'false');
    return;
  }
  
  // ✨ Narrows to: string | number
  if (typeof data === 'string' && data.length> 0) {
    // ✨ Narrows to: string (with length> 0 condition)
    console.log('Non-empty string:', data);
    return;
  }
  
  // ✨ Remaining possibilities: string (empty) | number
  console.log('Remaining data:', data);
}

🏗️ Type Narrowing Mechanisms

// 🎯 Typeof guards
function typeofGuards(value: unknown) {
  if (typeof value === 'string') {
    // ✨ Narrowed to string
    console.log(value.substring(0, 10));
  } else if (typeof value === 'number') {
    // ✨ Narrowed to number
    console.log(value.toFixed(2));
  } else if (typeof value === 'boolean') {
    // ✨ Narrowed to boolean
    console.log(value ? 'truthy' : 'falsy');
  } else {
    // ✨ Narrowed to unknown (excluding string, number, boolean)
    console.log('Other type:', value);
  }
}

// 🌟 Instanceof guards
class Dog {
  bark() { return 'Woof!'; }
}

class Cat {
  meow() { return 'Meow!'; }
}

function instanceofGuards(animal: Dog | Cat | string) {
  if (animal instanceof Dog) {
    // ✨ Narrowed to Dog
    console.log(animal.bark());
  } else if (animal instanceof Cat) {
    // ✨ Narrowed to Cat
    console.log(animal.meow());
  } else {
    // ✨ Narrowed to string
    console.log('Animal name:', animal);
  }
}

// 🔍 In operator guards
interface Bird {
  fly(): void;
  species: string;
}

interface Fish {
  swim(): void;
  species: string;
}

function inOperatorGuards(creature: Bird | Fish) {
  if ('fly' in creature) {
    // ✨ Narrowed to Bird
    creature.fly();
    console.log('Flying bird:', creature.species);
  } else {
    // ✨ Narrowed to Fish
    creature.swim();
    console.log('Swimming fish:', creature.species);
  }
}

// 🎨 Equality guards
function equalityGuards(value: 'red' | 'green' | 'blue' | null) {
  if (value === null) {
    // ✨ Narrowed to null
    console.log('No color');
    return;
  }
  
  // ✨ Narrowed to 'red' | 'green' | 'blue'
  switch (value) {
    case 'red':
      // ✨ Narrowed to 'red'
      console.log('Red color');
      break;
    case 'green':
      // ✨ Narrowed to 'green'
      console.log('Green color');
      break;
    case 'blue':
      // ✨ Narrowed to 'blue'
      console.log('Blue color');
      break;
    default:
      // ✨ Never reached (exhaustive)
      const exhaustive: never = value;
      throw new Error(`Unhandled color: ${exhaustive}`);
  }
}

// 🔄 Assignment flow analysis
function assignmentFlow() {
  let value: string | number;
  
  if (Math.random() > 0.5) {
    value = 'hello';
    // ✨ TypeScript knows value is string here
    console.log(value.toUpperCase());
  } else {
    value = 42;
    // ✨ TypeScript knows value is number here
    console.log(value.toFixed(2));
  }
  
  // ✨ TypeScript knows value is string | number here
  console.log('Value:', value);
}

🧮 Complex Flow Analysis Patterns

// 🎯 Nested conditions with type narrowing
interface User {
  id: number;
  name: string;
  email?: string;
  profile?: {
    age: number;
    bio?: string;
  };
}

function complexNarrowing(user: User | null | undefined) {
  if (!user) {
    // ✨ Narrowed to null | undefined
    console.log('No user');
    return;
  }
  
  // ✨ Narrowed to User
  console.log('User ID:', user.id);
  
  if (user.email) {
    // ✨ TypeScript knows email exists and is truthy
    console.log('Email:', user.email.toLowerCase());
  }
  
  if (user.profile) {
    // ✨ TypeScript knows profile exists
    console.log('Age:', user.profile.age);
    
    if (user.profile.bio) {
      // ✨ TypeScript knows bio exists and is truthy
      console.log('Bio length:', user.profile.bio.length);
    }
  }
}

// 🌟 Loop-based flow analysis
function loopFlowAnalysis(items: Array<string | number>) {
  for (const item of items) {
    if (typeof item === 'string') {
      // ✨ Narrowed to string for this iteration
      console.log('String item:', item.toUpperCase());
      continue;
    }
    
    // ✨ Narrowed to number for remaining code
    console.log('Number item:', item.toFixed(1));
  }
}

// 🔍 Function call flow analysis
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isNumber(value: unknown): value is number {
  return typeof value === 'number';
}

function functionCallFlow(value: unknown) {
  if (isString(value)) {
    // ✨ TypeScript uses the type predicate
    console.log('String value:', value.charAt(0));
    return;
  }
  
  if (isNumber(value)) {
    // ✨ TypeScript uses the type predicate
    console.log('Number value:', value.toPrecision(3));
    return;
  }
  
  // ✨ TypeScript knows it's neither string nor number
  console.log('Other value:', value);
}

// 🎨 Exception flow analysis
function exceptionFlow(value: string | null) {
  if (value === null) {
    throw new Error('Value cannot be null');
  }
  
  // ✨ TypeScript knows value is string (null case throws)
  console.log('String value:', value.substring(0, 5));
  
  try {
    const parsed = JSON.parse(value);
    // ✨ TypeScript knows parsing succeeded if we reach here
    console.log('Parsed value:', parsed);
  } catch (error) {
    // ✨ TypeScript knows parsing failed in this block
    console.log('Invalid JSON:', value);
  }
}

// 🔄 Short-circuit evaluation flow
function shortCircuitFlow(obj: { prop?: string } | null) {
  // && operator with nullish checking
  obj && obj.prop && console.log('Prop exists:', obj.prop.length);
  
  // || operator with fallback
  const value = obj?.prop || 'default';
  // ✨ TypeScript knows value is string
  console.log('Value length:', value.length);
  
  // Nullish coalescing
  const prop = obj?.prop ?? 'fallback';
  // ✨ TypeScript knows prop is string
  console.log('Prop value:', prop.toUpperCase());
}

🔄 Advanced Control Flow Patterns

🎯 Discriminated Unions and Exhaustiveness

// 🌟 Discriminated union flow analysis
interface LoadingState {
  status: 'loading';
  progress: number;
}

interface SuccessState {
  status: 'success';
  data: string[];
}

interface ErrorState {
  status: 'error';
  message: string;
  code: number;
}

type AppState = LoadingState | SuccessState | ErrorState;

function handleAppState(state: AppState) {
  // ✨ TypeScript tracks which union member we're in
  switch (state.status) {
    case 'loading':
      // ✨ Narrowed to LoadingState
      console.log(`Loading: ${state.progress}%`);
      if (state.progress === 100) {
        console.log('Almost done!');
      }
      break;
      
    case 'success':
      // ✨ Narrowed to SuccessState
      console.log(`Success with ${state.data.length} items`);
      state.data.forEach(item => console.log(item));
      break;
      
    case 'error':
      // ✨ Narrowed to ErrorState
      console.log(`Error ${state.code}: ${state.message}`);
      if (state.code>= 500) {
        console.log('Server error');
      }
      break;
      
    default:
      // ✨ TypeScript ensures exhaustiveness
      const exhaustive: never = state;
      throw new Error(`Unhandled state: ${exhaustive}`);
  }
}

// 🎯 Complex discriminated union
type Shape = 
  | { kind: 'circle'; radius: number }
  | { kind: 'rectangle'; width: number; height: number }
  | { kind: 'triangle'; base: number; height: number }
  | { kind: 'polygon'; sides: number; sideLength: number };

function calculateArea(shape: Shape): number {
  if (shape.kind === 'circle') {
    // ✨ Narrowed to circle
    return Math.PI * shape.radius ** 2;
  }
  
  if (shape.kind === 'rectangle') {
    // ✨ Narrowed to rectangle
    return shape.width * shape.height;
  }
  
  if (shape.kind === 'triangle') {
    // ✨ Narrowed to triangle
    return 0.5 * shape.base * shape.height;
  }
  
  // ✨ TypeScript knows it must be polygon
  const perimeter = shape.sides * shape.sideLength;
  // Approximate area for regular polygon
  return (perimeter * shape.sideLength) / (4 * Math.tan(Math.PI / shape.sides));
}

// 🌟 Nested discriminated unions
interface DatabaseError {
  type: 'database';
  code: 'connection' | 'timeout' | 'query';
  details: string;
}

interface ValidationError {
  type: 'validation';
  field: string;
  rule: 'required' | 'format' | 'length';
}

interface NetworkError {
  type: 'network';
  status: number;
  url: string;
}

type ApplicationError = DatabaseError | ValidationError | NetworkError;

function handleError(error: ApplicationError) {
  switch (error.type) {
    case 'database':
      // ✨ Narrowed to DatabaseError
      switch (error.code) {
        case 'connection':
          console.log('Database connection failed:', error.details);
          break;
        case 'timeout':
          console.log('Database timeout:', error.details);
          break;
        case 'query':
          console.log('Query error:', error.details);
          break;
      }
      break;
      
    case 'validation':
      // ✨ Narrowed to ValidationError
      console.log(`Validation error in ${error.field}: ${error.rule}`);
      break;
      
    case 'network':
      // ✨ Narrowed to NetworkError
      console.log(`Network error ${error.status} at ${error.url}`);
      break;
  }
}

🧩 Array and Object Flow Analysis

// 🎯 Array flow analysis
function arrayFlowAnalysis(items: Array<string | number | null>) {
  const validItems: Array<string | number> = [];
  
  for (const item of items) {
    if (item !== null) {
      // ✨ TypeScript narrows to string | number
      validItems.push(item);
    }
  }
  
  // ✨ TypeScript knows validItems contains no nulls
  validItems.forEach(item => {
    if (typeof item === 'string') {
      console.log('String:', item.toUpperCase());
    } else {
      console.log('Number:', item.toFixed(2));
    }
  });
}

// 🌟 Object property flow analysis
interface PartialUser {
  name?: string;
  age?: number;
  email?: string;
}

function objectFlowAnalysis(user: PartialUser) {
  // Check and narrow multiple properties
  if (user.name && user.email) {
    // ✨ TypeScript knows both name and email are defined
    console.log(`User: ${user.name} <${user.email}>`);
    
    // Optional property access with narrowing
    if (user.age) {
      // ✨ TypeScript knows age is defined
      console.log(`Age: ${user.age} years old`);
    }
  }
  
  // Object destructuring with default values
  const { name = 'Anonymous', age = 0, email = 'no-email' } = user;
  // ✨ TypeScript knows all variables are defined with their respective types
  console.log(`Name: ${name}, Age: ${age}, Email: ${email}`);
}

// 🔍 Array method flow analysis
function arrayMethodFlow(data: Array<string | number | null>) {
  // Filter with type narrowing
  const numbers = data.filter((item): item is number => typeof item === 'number');
  // ✨ TypeScript knows numbers is number[]
  
  const strings = data.filter((item): item is string => typeof item === 'string');
  // ✨ TypeScript knows strings is string[]
  
  // Map with type narrowing
  const lengths = strings.map(str => str.length);
  // ✨ TypeScript knows lengths is number[]
  
  // Find with type narrowing
  const firstLongString = strings.find(str => str.length> 10);
  // ✨ TypeScript knows firstLongString is string | undefined
  
  if (firstLongString) {
    // ✨ TypeScript narrows to string
    console.log('Long string:', firstLongString.toUpperCase());
  }
}

// 🎨 Reduce flow analysis
function reduceFlowAnalysis(items: Array<{ type: 'add' | 'subtract'; value: number }>) {
  const result = items.reduce((acc, item) => {
    // ✨ TypeScript knows item structure from array type
    switch (item.type) {
      case 'add':
        return acc + item.value;
      case 'subtract':
        return acc - item.value;
      default:
        // ✨ Exhaustiveness checking
        const exhaustive: never = item.type;
        throw new Error(`Unknown operation: ${exhaustive}`);
    }
  }, 0);
  
  // ✨ TypeScript knows result is number
  return result;
}

🔄 Async Control Flow Analysis

// 🎯 Promise flow analysis
async function promiseFlowAnalysis(url: string): Promise<string | null> {
  try {
    const response = await fetch(url);
    
    if (!response.ok) {
      // ✨ TypeScript knows response failed
      console.log('Request failed:', response.status);
      return null;
    }
    
    // ✨ TypeScript knows response is ok
    const text = await response.text();
    // ✨ TypeScript knows text is string
    return text.trim();
    
  } catch (error) {
    // ✨ TypeScript knows an error occurred
    console.log('Network error:', error);
    return null;
  }
}

// 🌟 Promise chaining flow
function promiseChainingFlow(data: unknown) {
  return Promise.resolve(data)
    .then(value => {
      if (typeof value === 'string') {
        // ✨ Narrowed to string
        return value.toUpperCase();
      }
      throw new Error('Expected string');
    })
    .then(upperCase => {
      // ✨ TypeScript knows upperCase is string
      return upperCase.split('');
    })
    .catch(error => {
      // ✨ TypeScript knows error occurred
      console.log('Processing error:', error);
      return [];
    });
  // ✨ Final return type is Promise<string[]>
}

// 🔍 Async iteration flow
async function asyncIterationFlow(urls: string[]) {
  const results: string[] = [];
  
  for (const url of urls) {
    try {
      const data = await promiseFlowAnalysis(url);
      
      if (data !== null) {
        // ✨ TypeScript narrows to string
        results.push(data);
      }
    } catch (error) {
      console.log(`Failed to process ${url}:`, error);
    }
  }
  
  // ✨ TypeScript knows results is string[]
  return results;
}

// 🎨 Conditional async flow
async function conditionalAsyncFlow(condition: boolean, value: string) {
  if (condition) {
    // Async branch
    const processed = await new Promise<string>(resolve => {
      setTimeout(() => resolve(value.toUpperCase()), 100);
    });
    
    // ✨ TypeScript knows processed is string
    return processed;
  } else {
    // Sync branch
    const processed = value.toLowerCase();
    
    // ✨ TypeScript knows processed is string
    return processed;
  }
  
  // ✨ Both branches return string, so return type is Promise<string>
}

🛡️ Advanced Flow Analysis Techniques

🔍 Custom Type Guards with Flow Analysis

// 🌟 Complex type guard compositions
interface AdminUser {
  role: 'admin';
  permissions: string[];
  accessLevel: number;
}

interface RegularUser {
  role: 'user';
  preferences: object;
}

interface GuestUser {
  role: 'guest';
  sessionId: string;
}

type User = AdminUser | RegularUser | GuestUser;

function isAdmin(user: User): user is AdminUser {
  return user.role === 'admin';
}

function isRegular(user: User): user is RegularUser {
  return user.role === 'user';
}

function hasHighAccess(user: User): user is AdminUser {
  return isAdmin(user) && user.accessLevel>= 5;
}

function complexUserFlow(user: User) {
  if (hasHighAccess(user)) {
    // ✨ TypeScript knows user is AdminUser with accessLevel>= 5
    console.log('High access admin:', user.permissions);
    return;
  }
  
  if (isAdmin(user)) {
    // ✨ TypeScript knows user is AdminUser (but accessLevel < 5)
    console.log('Regular admin:', user.accessLevel);
    return;
  }
  
  if (isRegular(user)) {
    // ✨ TypeScript knows user is RegularUser
    console.log('Regular user preferences:', user.preferences);
    return;
  }
  
  // ✨ TypeScript knows user is GuestUser
  console.log('Guest session:', user.sessionId);
}

// 🎯 Assertion function flow analysis
function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== 'string') {
    throw new Error('Expected string');
  }
}

function assertIsArray<T>(value: unknown): asserts value is T[] {
  if (!Array.isArray(value)) {
    throw new Error('Expected array');
  }
}

function assertionFlow(data: unknown) {
  assertIsArray(data);
  // ✨ TypeScript knows data is unknown[]
  
  if (data.length> 0) {
    assertIsString(data[0]);
    // ✨ TypeScript knows data[0] is string
    console.log('First item:', data[0].toUpperCase());
  }
  
  // ✨ TypeScript still knows data is unknown[]
  data.forEach((item, index) => {
    try {
      assertIsString(item);
      // ✨ Inside try, TypeScript knows item is string
      console.log(`Item ${index}:`, item.length);
    } catch {
      // ✨ TypeScript knows assertion failed
      console.log(`Item ${index} is not a string`);
    }
  });
}

🎨 Flow Analysis with Generic Types

// 🌟 Generic flow analysis
function genericFlowAnalysis<T>(
  value: T,
  predicate: (val: T) => val is T extends string ? string : never
): value is T extends string ? string : never {
  return predicate(value);
}

function processGenericValue<T extends string | number>(value: T | null) {
  if (value === null) {
    // ✨ TypeScript narrows to null
    console.log('Value is null');
    return;
  }
  
  // ✨ TypeScript narrows to T
  if (typeof value === 'string') {
    // ✨ TypeScript narrows to T & string
    console.log('String value:', value.toUpperCase());
    return value as T & string;
  }
  
  // ✨ TypeScript narrows to T & number
  console.log('Number value:', value.toFixed(2));
  return value as T & number;
}

// 🎯 Conditional type flow analysis
type ProcessResult<T> = T extends string 
  ? { type: 'string'; value: string; length: number }
  : T extends number 
    ? { type: 'number'; value: number; isInteger: boolean }
    : { type: 'other'; value: T };

function processWithConditionalType<T>(input: T): ProcessResult<T> {
  if (typeof input === 'string') {
    // ✨ TypeScript infers the conditional type result
    return {
      type: 'string',
      value: input,
      length: input.length
    } as ProcessResult<T>;
  }
  
  if (typeof input === 'number') {
    // ✨ TypeScript infers the conditional type result
    return {
      type: 'number',
      value: input,
      isInteger: Number.isInteger(input)
    } as ProcessResult<T>;
  }
  
  // ✨ TypeScript infers the fallback case
  return {
    type: 'other',
    value: input
  } as ProcessResult<T>;
}

// 🔍 Mapped type flow analysis
type RequiredFields<T, K extends keyof T> = T & Required<Pick<T, K>>;

function validateRequiredFields<T extends object, K extends keyof T>(
  obj: T,
  requiredKeys: K[]
): obj is RequiredFields<T, K> {
  return requiredKeys.every(key => obj[key] !== undefined && obj[key] !== null);
}

interface PartialData {
  name?: string;
  age?: number;
  email?: string;
}

function mappedTypeFlow(data: PartialData) {
  if (validateRequiredFields(data, ['name', 'email'])) {
    // ✨ TypeScript knows name and email are required
    console.log(`User: ${data.name} <${data.email}>`);
    
    // age is still optional
    if (data.age) {
      console.log(`Age: ${data.age}`);
    }
  }
}

🔄 Complex State Machine Flow

// 🎯 State machine with flow analysis
interface IdleState {
  status: 'idle';
  lastAction: string | null;
}

interface LoadingState {
  status: 'loading';
  progress: number;
  startTime: number;
}

interface ProcessingState {
  status: 'processing';
  stage: 'validation' | 'transformation' | 'saving';
  data: unknown;
}

interface CompletedState {
  status: 'completed';
  result: string;
  duration: number;
}

interface ErrorState {
  status: 'error';
  message: string;
  code: number;
  retryable: boolean;
}

type MachineState = IdleState | LoadingState | ProcessingState | CompletedState | ErrorState;

class StateMachine {
  private state: MachineState = { status: 'idle', lastAction: null };
  
  transition(action: string, payload?: any): void {
    const currentStatus = this.state.status;
    
    switch (currentStatus) {
      case 'idle':
        // ✨ TypeScript knows this.state is IdleState
        if (action === 'start') {
          this.state = {
            status: 'loading',
            progress: 0,
            startTime: Date.now()
          };
        }
        break;
        
      case 'loading':
        // ✨ TypeScript knows this.state is LoadingState
        if (action === 'progress') {
          this.state = {
            ...this.state,
            progress: payload.progress
          };
        } else if (action === 'complete_loading') {
          this.state = {
            status: 'processing',
            stage: 'validation',
            data: payload.data
          };
        }
        break;
        
      case 'processing':
        // ✨ TypeScript knows this.state is ProcessingState
        if (action === 'next_stage') {
          const nextStage = this.getNextStage(this.state.stage);
          if (nextStage) {
            this.state = {
              ...this.state,
              stage: nextStage
            };
          } else {
            this.state = {
              status: 'completed',
              result: 'Processing finished',
              duration: Date.now() - this.getStartTime()
            };
          }
        }
        break;
        
      case 'completed':
        // ✨ TypeScript knows this.state is CompletedState
        if (action === 'reset') {
          this.state = {
            status: 'idle',
            lastAction: 'reset'
          };
        }
        break;
        
      case 'error':
        // ✨ TypeScript knows this.state is ErrorState
        if (action === 'retry' && this.state.retryable) {
          this.state = {
            status: 'idle',
            lastAction: 'retry'
          };
        }
        break;
    }
  }
  
  private getNextStage(current: ProcessingState['stage']): ProcessingState['stage'] | null {
    switch (current) {
      case 'validation':
        return 'transformation';
      case 'transformation':
        return 'saving';
      case 'saving':
        return null;
    }
  }
  
  private getStartTime(): number {
    // This would need more sophisticated tracking in real implementation
    return Date.now() - 1000;
  }
  
  getState(): MachineState {
    return this.state;
  }
  
  canTransition(action: string): boolean {
    switch (this.state.status) {
      case 'idle':
        return action === 'start';
      case 'loading':
        return ['progress', 'complete_loading', 'error'].includes(action);
      case 'processing':
        return ['next_stage', 'error'].includes(action);
      case 'completed':
        return action === 'reset';
      case 'error':
        return this.state.retryable && action === 'retry';
      default:
        // ✨ Exhaustiveness checking
        const exhaustive: never = this.state;
        return false;
    }
  }
}

// Usage with flow analysis
function useStateMachine() {
  const machine = new StateMachine();
  const state = machine.getState();
  
  // ✨ TypeScript provides intelligent narrowing
  switch (state.status) {
    case 'loading':
      console.log(`Loading: ${state.progress}%`);
      if (state.progress>= 100) {
        machine.transition('complete_loading', { data: 'sample data' });
      }
      break;
      
    case 'processing':
      console.log(`Processing stage: ${state.stage}`);
      if (state.stage === 'saving') {
        machine.transition('next_stage');
      }
      break;
      
    case 'error':
      console.log(`Error: ${state.message} (Code: ${state.code})`);
      if (state.retryable) {
        machine.transition('retry');
      }
      break;
  }
}

🎓 Key Takeaways

You’ve mastered TypeScript’s control flow analysis engine! Here’s what you now command:

  • Deep understanding of how TypeScript tracks types through code paths 💪
  • Advanced type narrowing with guards, assertions, and conditions 🛡️
  • Complex flow patterns including discriminated unions and state machines 🎯
  • Async flow analysis with promises and error handling 🐛
  • Generic type flow with conditional and mapped types 🚀
  • Optimization techniques for better type inference and safety ✨
  • Real-world applications in complex codebases and architectures 🔄

Remember: Understanding control flow analysis helps you write code that works harmoniously with TypeScript’s intelligence! 🤝

🤝 Next Steps

Congratulations! 🎉 You’ve become a master of TypeScript’s control flow analysis!

Here’s what to do next:

  1. 💻 Design code structures that optimize TypeScript’s flow analysis
  2. 🏗️ Create sophisticated type guards and assertion systems
  3. 📚 Explore more advanced TypeScript features and patterns
  4. 🌟 Build complex state machines and control flow systems
  5. 🔍 Optimize existing codebases for better type inference
  6. 🎯 Contribute to TypeScript tooling and analysis systems
  7. 🚀 Push the boundaries of what’s possible with type-level programming

Remember: You now understand the inner workings of TypeScript’s type tracking! Use this knowledge to write incredibly intelligent and type-safe applications that leverage the full power of the compiler. 🚀


Happy flow analyzing! 🎉🌊✨