+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 86 of 354

๐Ÿ‘ป Phantom Types: Compile-Time Only Types

Master phantom types in TypeScript to create zero-cost type abstractions that exist only at compile time for ultimate type safety and constraint modeling ๐Ÿš€

๐Ÿ’ŽAdvanced
30 min read

Prerequisites

  • Advanced understanding of TypeScript generics and type parameters ๐Ÿ“
  • Experience with brand types and nominal typing patterns โšก
  • Knowledge of type-level programming and constraints ๐Ÿ’ป

What you'll learn

  • Understand phantom types and zero-cost abstractions ๐ŸŽฏ
  • Implement compile-time constraints with phantom parameters ๐Ÿ—๏ธ
  • Build type-safe state machines and protocols ๐Ÿ›
  • Apply phantom types for domain modeling and API design โœจ

๐ŸŽฏ Introduction

Welcome to the ghostly realm of phantom types in TypeScript! ๐Ÿ‘ป This tutorial explores one of the most elegant type system patterns: phantom types - type parameters that exist purely at compile time and vanish at runtime, leaving zero performance impact while providing maximum type safety.

Youโ€™ll discover how phantom types allow you to encode constraints, state transitions, and domain logic directly into the type system. Whether youโ€™re building state machines ๐ŸŽฐ, modeling units of measurement ๐Ÿ“, or creating APIs with protocol safety ๐Ÿ”’, phantom types give you the power to make invalid states unrepresentable at compile time.

By the end of this tutorial, youโ€™ll be conjuring type ghosts that guard your code with invisible, zero-cost protection! Letโ€™s explore the phantom dimension of TypeScript! ๐ŸŒŸ

๐Ÿ“š Understanding Phantom Types

๐Ÿค” What are Phantom Types?

Phantom types are type parameters that appear in a type definition but are never actually used in the runtime implementation ๐Ÿ‘ป. They exist purely to carry type-level information, constraints, or state information that the compiler can enforce.

Key characteristics:

  • โœจ Zero Runtime Cost: No actual data or behavior at runtime
  • ๐Ÿš€ Compile-Time Safety: Enforce constraints during type checking
  • ๐Ÿ›ก๏ธ State Encoding: Represent state, units, or protocol information
  • ๐Ÿ”„ Type-Level Computation: Enable complex type-level logic

๐Ÿ’ก Phantom vs Regular Type Parameters

Hereโ€™s how phantom types differ from regular generic parameters:

// ๐ŸŽฏ Regular generic type - T is used in implementation
interface Container<T> {
  value: T;  // โœ… T is used here
  getValue(): T;  // โœ… And here
}

// ๐Ÿ‘ป Phantom type - P is never used in implementation
interface PhantomContainer<T, P> {
  value: T;  // โœ… T is used
  // P is nowhere to be found! ๐Ÿ‘ป
  getValue(): T;
}

// ๐ŸŽจ Example usage
type StringContainer = Container<string>;
type ValidatedString = PhantomContainer<string, 'validated'>;
type UnsafeString = PhantomContainer<string, 'unsafe'>;

// โœจ Same runtime structure, different compile-time identity
const validated: ValidatedString = { value: "safe", getValue: () => "safe" };
const unsafe: UnsafeString = { value: "risky", getValue: () => "risky" };

// โŒ This prevents dangerous mixing at compile time:
// const mixed: ValidatedString = unsafe; // Error: different phantom types!

๐Ÿ—๏ธ Benefits of Phantom Types

Hereโ€™s why phantom types are powerful for advanced TypeScript development:

  1. Zero-Cost Abstractions โšก: No runtime overhead whatsoever
  2. State Machine Modeling ๐ŸŽฐ: Track state transitions in types
  3. Unit System Safety ๐Ÿ“: Prevent unit mixing (meters vs feet)
  4. Protocol Enforcement ๐Ÿ”’: Ensure correct API usage patterns
  5. Domain Modeling ๐Ÿข: Encode business rules in the type system
  6. API Evolution ๐Ÿ”„: Add constraints without breaking existing code

Real-world application: A database connection type that tracks whether itโ€™s connected, transaction state, and authorization level - all at compile time! ๐Ÿ—„๏ธ

๐Ÿ‘ป Basic Phantom Type Patterns

๐ŸŽฏ Simple Phantom Parameter

// ๐ŸŒŸ Basic phantom type pattern
type Phantom<T, P> = T & {
  readonly __phantom: P;
};

// ๐ŸŽจ Never actually create the phantom field
type BrandWith<T, P> = T & {
  readonly __brand: P;
};

// ๐Ÿ‘ป Unit system with phantom types
type Length<Unit> = number & { readonly __unit: Unit };

type Meters = Length<'meters'>;
type Feet = Length<'feet'>;
type Inches = Length<'inches'>;

// ๐Ÿ—๏ธ Smart constructors
const meters = (value: number): Meters => value as Meters;
const feet = (value: number): Feet => value as Feet;
const inches = (value: number): Inches => value as Inches;

// โœจ Type-safe operations
function addMeters(a: Meters, b: Meters): Meters {
  return meters((a as number) + (b as number));
}

function addFeet(a: Feet, b: Feet): Feet {
  return feet((a as number) + (a as number));
}

// ๐Ÿงช Usage examples
const length1 = meters(100);
const length2 = meters(50);
const height1 = feet(6);
const height2 = feet(3);

// โœ… These work - same units
const totalLength = addMeters(length1, length2);  // โœ… Meters + Meters
const totalHeight = addFeet(height1, height2);    // โœ… Feet + Feet

// โŒ These are compile-time errors - unit mixing prevention!
// const mixed = addMeters(length1, height1);  // โŒ Error: Can't mix meters and feet
// const invalid = addFeet(length1, height2);  // โŒ Error: Can't mix meters and feet

๐Ÿ”„ Unit Conversion with Phantom Types

// ๐ŸŽฏ Advanced unit system with conversions
type Temperature<Scale> = number & { readonly __scale: Scale };

type Celsius = Temperature<'celsius'>;
type Fahrenheit = Temperature<'fahrenheit'>;
type Kelvin = Temperature<'kelvin'>;

// ๐Ÿ—๏ธ Temperature constructors with validation
const celsius = (value: number): Celsius => {
  if (value < -273.15) {
    throw new Error("Temperature below absolute zero");
  }
  return value as Celsius;
};

const fahrenheit = (value: number): Fahrenheit => {
  if (value < -459.67) {
    throw new Error("Temperature below absolute zero");
  }
  return value as Fahrenheit;
};

const kelvin = (value: number): Kelvin => {
  if (value < 0) {
    throw new Error("Temperature below absolute zero");
  }
  return value as Kelvin;
};

// ๐Ÿ”„ Type-safe conversions
function celsiusToFahrenheit(temp: Celsius): Fahrenheit {
  const fahrenheitValue = (temp as number) * 9/5 + 32;
  return fahrenheit(fahrenheitValue);
}

function fahrenheitToCelsius(temp: Fahrenheit): Celsius {
  const celsiusValue = ((temp as number) - 32) * 5/9;
  return celsius(celsiusValue);
}

function celsiusToKelvin(temp: Celsius): Kelvin {
  const kelvinValue = (temp as number) + 273.15;
  return kelvin(kelvinValue);
}

function kelvinToCelsius(temp: Kelvin): Celsius {
  const celsiusValue = (temp as number) - 273.15;
  return celsius(celsiusValue);
}

// ๐ŸŒก๏ธ Temperature comparison (same scale only)
function compareTemperatures<Scale>(
  a: Temperature<Scale>, 
  b: Temperature<Scale>
): 'hotter' | 'colder' | 'same' {
  const aValue = a as number;
  const bValue = b as number;
  return aValue> bValue ? 'hotter' : aValue < bValue ? 'colder' : 'same';
}

// ๐Ÿงช Temperature system usage
const roomTemp = celsius(22);
const bodyTemp = fahrenheit(98.6);
const absoluteZero = kelvin(0);

// โœ… Type-safe conversions
const roomTempF = celsiusToFahrenheit(roomTemp);
const bodyTempC = fahrenheitToCelsius(bodyTemp);
const roomTempK = celsiusToKelvin(roomTemp);

// โœ… Same-scale comparisons
const celsiusComparison = compareTemperatures(roomTemp, bodyTempC);
const fahrenheitComparison = compareTemperatures(roomTempF, bodyTemp);

// โŒ These would be compile-time errors:
// const invalidComparison = compareTemperatures(roomTemp, bodyTemp);  // Different scales!
// const invalidConversion = celsiusToFahrenheit(bodyTemp);  // Wrong input type!

console.log(`Room temp in F: ${roomTempF}ยฐF`);
console.log(`Body temp in C: ${bodyTempC}ยฐC`);
console.log(`Comparison: Room temp is ${celsiusComparison} than body temp`);

๐ŸŽฐ State Machine Modeling

๐Ÿšฆ Connection State Machine

// ๐ŸŽฐ Connection states as phantom types
type ConnectionState = 'Disconnected' | 'Connecting' | 'Connected' | 'Reconnecting' | 'Failed';

type Connection<State extends ConnectionState> = {
  readonly id: string;
  readonly host: string;
  readonly port: number;
  readonly createdAt: Date;
} & { readonly __state: State };

// ๐Ÿ—๏ธ State-specific constructors
function createConnection(host: string, port: number): Connection<'Disconnected'> {
  return {
    id: `conn_${Date.now()}`,
    host,
    port,
    createdAt: new Date(),
    __state: 'Disconnected' as const
  };
}

// ๐Ÿ”„ State transition functions
function startConnecting(conn: Connection<'Disconnected'>): Connection<'Connecting'> {
  console.log(`Starting connection to ${conn.host}:${conn.port}`);
  return { ...conn, __state: 'Connecting' as const };
}

function completeConnection(conn: Connection<'Connecting'>): Connection<'Connected'> {
  console.log(`Connection established to ${conn.host}:${conn.port}`);
  return { ...conn, __state: 'Connected' as const };
}

function startReconnecting(conn: Connection<'Connected' | 'Failed'>): Connection<'Reconnecting'> {
  console.log(`Reconnecting to ${conn.host}:${conn.port}`);
  return { ...conn, __state: 'Reconnecting' as const };
}

function failConnection(
  conn: Connection<'Connecting' | 'Reconnecting'>
): Connection<'Failed'> {
  console.log(`Connection failed to ${conn.host}:${conn.port}`);
  return { ...conn, __state: 'Failed' as const };
}

function disconnect(conn: Connection<'Connected'>): Connection<'Disconnected'> {
  console.log(`Disconnected from ${conn.host}:${conn.port}`);
  return { ...conn, __state: 'Disconnected' as const };
}

// ๐ŸŽฏ State-specific operations
function sendData(conn: Connection<'Connected'>, data: string): void {
  console.log(`Sending data: ${data}`);
  // Only connected connections can send data!
}

function getConnectionInfo(conn: Connection<'Connected'>): string {
  return `Connected to ${conn.host}:${conn.port} since ${conn.createdAt}`;
}

function canReconnect(conn: Connection<ConnectionState>): conn is Connection<'Failed' | 'Connected'> {
  const state = conn.__state;
  return state === 'Failed' || state === 'Connected';
}

// ๐Ÿงช Type-safe connection workflow
const connection = createConnection("api.example.com", 443);
console.log("๐ŸŽฏ Initial state: Disconnected");

// โœ… Valid state transitions
const connecting = startConnecting(connection);
const connected = completeConnection(connecting);

// โœ… Operations only work in correct states
sendData(connected, "Hello, server!");
console.log(getConnectionInfo(connected));

// ๐Ÿ”„ Reconnection workflow
const reconnecting = startReconnecting(connected);
const failed = failConnection(reconnecting);

if (canReconnect(failed)) {
  const reconnectingAgain = startReconnecting(failed);
  console.log("๐Ÿ”„ Attempting reconnection...");
}

// โŒ These would be compile-time errors:
// sendData(connecting, "data");        // โŒ Can't send on connecting connection
// const invalid = completeConnection(connected);  // โŒ Already connected
// const badTransition = startConnecting(connected);  // โŒ Invalid state transition

๐Ÿ” Authentication State Machine

// ๐Ÿ”’ Authentication states
type AuthState = 'Unauthenticated' | 'Authenticating' | 'Authenticated' | 'Expired' | 'Locked';

type AuthSession<State extends AuthState> = {
  readonly sessionId: string;
  readonly userId?: string;
  readonly permissions?: string[];
  readonly expiresAt?: Date;
  readonly loginAttempts: number;
} & { readonly __authState: State };

// ๐Ÿ—๏ธ Authentication constructors
function createUnauthenticatedSession(): AuthSession<'Unauthenticated'> {
  return {
    sessionId: `session_${Date.now()}`,
    loginAttempts: 0,
    __authState: 'Unauthenticated' as const
  };
}

// ๐Ÿ”„ Authentication transitions
function startAuthentication(
  session: AuthSession<'Unauthenticated'>
): AuthSession<'Authenticating'> {
  return {
    ...session,
    loginAttempts: session.loginAttempts + 1,
    __authState: 'Authenticating' as const
  };
}

function completeAuthentication(
  session: AuthSession<'Authenticating'>,
  userId: string,
  permissions: string[]
): AuthSession<'Authenticated'> {
  return {
    ...session,
    userId,
    permissions,
    expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
    __authState: 'Authenticated' as const
  };
}

function expireSession(session: AuthSession<'Authenticated'>): AuthSession<'Expired'> {
  return {
    ...session,
    __authState: 'Expired' as const
  };
}

function lockSession(
  session: AuthSession<'Unauthenticated'>
): AuthSession<'Locked'> {
  if (session.loginAttempts>= 3) {
    return {
      ...session,
      __authState: 'Locked' as const
    };
  }
  throw new Error("Session doesn't meet lock criteria");
}

function refreshSession(session: AuthSession<'Expired'>): AuthSession<'Authenticated'> {
  if (!session.userId || !session.permissions) {
    throw new Error("Cannot refresh session without user data");
  }
  
  return {
    ...session,
    expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
    __authState: 'Authenticated' as const
  };
}

// ๐Ÿ›ก๏ธ Protected operations
function authorizeAction(
  session: AuthSession<'Authenticated'>,
  requiredPermission: string
): boolean {
  return session.permissions!.includes(requiredPermission);
}

function getUserId(session: AuthSession<'Authenticated'>): string {
  return session.userId!; // Safe to use ! because type guarantees it exists
}

function performAdminAction(session: AuthSession<'Authenticated'>): void {
  if (authorizeAction(session, 'admin')) {
    console.log("๐Ÿ”ง Performing admin action");
  } else {
    throw new Error("Insufficient permissions");
  }
}

// ๐Ÿงช Authentication workflow
let session = createUnauthenticatedSession();
console.log("๐Ÿ‘ค Created unauthenticated session");

// โœ… Authentication flow
const authenticating = startAuthentication(session);
const authenticated = completeAuthentication(
  authenticating, 
  "user123", 
  ["read", "write", "admin"]
);

console.log(`๐ŸŽฏ User ${getUserId(authenticated)} authenticated`);

// โœ… Protected operations
if (authorizeAction(authenticated, "admin")) {
  performAdminAction(authenticated);
}

// ๐Ÿ”„ Session lifecycle
const expired = expireSession(authenticated);
const refreshed = refreshSession(expired);

console.log("๐Ÿ”„ Session refreshed successfully");

// โŒ These would be compile-time errors:
// getUserId(session);                    // โŒ Unauthenticated session has no user
// performAdminAction(authenticating);    // โŒ Not yet authenticated
// refreshSession(authenticated);         // โŒ Can't refresh non-expired session

๐Ÿ“Š Protocol and API Safety

๐ŸŒ HTTP Request State Tracking

// ๐ŸŒ HTTP request lifecycle states
type RequestState = 'Prepared' | 'Sent' | 'Received' | 'Parsed' | 'Error';

type HttpRequest<State extends RequestState, TResponse = unknown> = {
  readonly url: string;
  readonly method: 'GET' | 'POST' | 'PUT' | 'DELETE';
  readonly headers: Record<string, string>;
  readonly body?: string;
  readonly response?: TResponse;
  readonly error?: Error;
  readonly sentAt?: Date;
  readonly receivedAt?: Date;
} & { readonly __requestState: State };

// ๐Ÿ—๏ธ Request builders
function createRequest(
  url: string, 
  method: 'GET' | 'POST' | 'PUT' | 'DELETE'
): HttpRequest<'Prepared'> {
  return {
    url,
    method,
    headers: {},
    __requestState: 'Prepared' as const
  };
}

function addHeader<State extends RequestState>(
  request: HttpRequest<State>,
  key: string,
  value: string
): HttpRequest<State> {
  return {
    ...request,
    headers: { ...request.headers, [key]: value }
  };
}

function addBody(
  request: HttpRequest<'Prepared'>,
  body: string
): HttpRequest<'Prepared'> {
  if (request.method === 'GET') {
    throw new Error("GET requests cannot have a body");
  }
  return { ...request, body };
}

// ๐Ÿš€ Request execution
async function sendRequest(
  request: HttpRequest<'Prepared'>
): Promise<HttpRequest<'Sent'>> {
  console.log(`๐Ÿš€ Sending ${request.method} request to ${request.url}`);
  
  return {
    ...request,
    sentAt: new Date(),
    __requestState: 'Sent' as const
  };
}

async function receiveResponse<TResponse>(
  request: HttpRequest<'Sent'>
): Promise<HttpRequest<'Received', TResponse>> {
  // Simulate network delay
  await new Promise(resolve => setTimeout(resolve, 100));
  
  console.log(`๐Ÿ“ฅ Received response for ${request.url}`);
  
  // Simulate response (in real implementation, use fetch)
  const mockResponse = { status: 200, data: "Mock response" } as TResponse;
  
  return {
    ...request,
    response: mockResponse,
    receivedAt: new Date(),
    __requestState: 'Received' as const
  };
}

function parseResponse<TResponse, TParsed>(
  request: HttpRequest<'Received', TResponse>,
  parser: (response: TResponse) => TParsed
): HttpRequest<'Parsed', TParsed> {
  try {
    const parsed = parser(request.response!);
    
    return {
      ...request,
      response: parsed,
      __requestState: 'Parsed' as const
    };
  } catch (error) {
    throw new Error(`Failed to parse response: ${error.message}`);
  }
}

function handleError(
  request: HttpRequest<'Sent'>,
  error: Error
): HttpRequest<'Error'> {
  return {
    ...request,
    error,
    __requestState: 'Error' as const
  };
}

// ๐ŸŽฏ Type-safe response access
function getResponseData<TResponse>(
  request: HttpRequest<'Parsed', TResponse>
): TResponse {
  return request.response!; // Safe because type guarantees response exists
}

function getTimings(
  request: HttpRequest<'Received' | 'Parsed', any>
): { duration: number; sentAt: Date; receivedAt: Date } {
  return {
    duration: request.receivedAt!.getTime() - request.sentAt!.getTime(),
    sentAt: request.sentAt!,
    receivedAt: request.receivedAt!
  };
}

// ๐Ÿงช HTTP request workflow
interface UserData {
  id: number;
  name: string;
  email: string;
}

async function fetchUserWorkflow() {
  // โœ… Build request step by step
  let request = createRequest("https://api.example.com/users/1", "GET");
  request = addHeader(request, "Authorization", "Bearer token123");
  request = addHeader(request, "Accept", "application/json");
  
  try {
    // โœ… Send and receive
    const sent = await sendRequest(request);
    const received = await receiveResponse<{ user: UserData }>(sent);
    
    // โœ… Parse response with type safety
    const parsed = parseResponse(received, (response) => response.user);
    
    // โœ… Access parsed data safely
    const userData = getResponseData(parsed);
    const timings = getTimings(parsed);
    
    console.log(`๐Ÿ‘ค User: ${userData.name} (${userData.email})`);
    console.log(`โฑ๏ธ Request took ${timings.duration}ms`);
    
    return userData;
    
  } catch (error) {
    console.error("โŒ Request failed:", error.message);
    throw error;
  }
}

// โŒ These would be compile-time errors:
// getResponseData(request);              // โŒ Not yet parsed
// addBody(createRequest("/", "GET"), ""); // โŒ GET can't have body
// parseResponse(request, x => x);        // โŒ Not yet received

๐Ÿ”’ Database Transaction Safety

// ๐Ÿ—„๏ธ Database transaction states
type TransactionState = 'Started' | 'Active' | 'Committed' | 'RolledBack' | 'Failed';

type DatabaseTransaction<State extends TransactionState> = {
  readonly id: string;
  readonly startedAt: Date;
  readonly operations: string[];
  readonly isolationLevel: 'READ_UNCOMMITTED' | 'READ_COMMITTED' | 'REPEATABLE_READ' | 'SERIALIZABLE';
  readonly committedAt?: Date;
  readonly rolledBackAt?: Date;
  readonly error?: Error;
} & { readonly __transactionState: State };

// ๐Ÿ—๏ธ Transaction lifecycle
function beginTransaction(
  isolationLevel: 'READ_UNCOMMITTED' | 'READ_COMMITTED' | 'REPEATABLE_READ' | 'SERIALIZABLE' = 'READ_COMMITTED'
): DatabaseTransaction<'Started'> {
  return {
    id: `txn_${Date.now()}`,
    startedAt: new Date(),
    operations: [],
    isolationLevel,
    __transactionState: 'Started' as const
  };
}

function activateTransaction(
  transaction: DatabaseTransaction<'Started'>
): DatabaseTransaction<'Active'> {
  console.log(`๐ŸŽฏ Transaction ${transaction.id} activated`);
  return {
    ...transaction,
    __transactionState: 'Active' as const
  };
}

// ๐Ÿ”„ Transaction operations
function executeOperation(
  transaction: DatabaseTransaction<'Active'>,
  operation: string
): DatabaseTransaction<'Active'> {
  console.log(`๐Ÿ“ Executing: ${operation}`);
  return {
    ...transaction,
    operations: [...transaction.operations, operation]
  };
}

function commitTransaction(
  transaction: DatabaseTransaction<'Active'>
): DatabaseTransaction<'Committed'> {
  console.log(`โœ… Committing transaction ${transaction.id}`);
  return {
    ...transaction,
    committedAt: new Date(),
    __transactionState: 'Committed' as const
  };
}

function rollbackTransaction(
  transaction: DatabaseTransaction<'Active'>
): DatabaseTransaction<'RolledBack'> {
  console.log(`๐Ÿ”„ Rolling back transaction ${transaction.id}`);
  return {
    ...transaction,
    rolledBackAt: new Date(),
    __transactionState: 'RolledBack' as const
  };
}

function failTransaction(
  transaction: DatabaseTransaction<'Active'>,
  error: Error
): DatabaseTransaction<'Failed'> {
  console.log(`โŒ Transaction ${transaction.id} failed: ${error.message}`);
  return {
    ...transaction,
    error,
    __transactionState: 'Failed' as const
  };
}

// ๐ŸŽฏ Transaction-specific operations
function getTransactionSummary(
  transaction: DatabaseTransaction<'Committed' | 'RolledBack' | 'Failed'>
): {
  id: string;
  duration: number;
  operationCount: number;
  outcome: 'committed' | 'rolled_back' | 'failed';
} {
  const endTime = transaction.committedAt || transaction.rolledBackAt || new Date();
  
  return {
    id: transaction.id,
    duration: endTime.getTime() - transaction.startedAt.getTime(),
    operationCount: transaction.operations.length,
    outcome: transaction.__transactionState === 'Committed' ? 'committed' :
             transaction.__transactionState === 'RolledBack' ? 'rolled_back' : 'failed'
  };
}

function canRollback(
  transaction: DatabaseTransaction<TransactionState>
): transaction is DatabaseTransaction<'Active'> {
  return transaction.__transactionState === 'Active';
}

// ๐Ÿงช Database transaction workflow
async function transferMoney(fromAccount: string, toAccount: string, amount: number) {
  let transaction = beginTransaction('SERIALIZABLE');
  transaction = activateTransaction(transaction);
  
  try {
    // โœ… Execute operations within transaction
    transaction = executeOperation(
      transaction, 
      `UPDATE accounts SET balance = balance - ${amount} WHERE id = '${fromAccount}'`
    );
    
    transaction = executeOperation(
      transaction,
      `UPDATE accounts SET balance = balance + ${amount} WHERE id = '${toAccount}'`
    );
    
    transaction = executeOperation(
      transaction,
      `INSERT INTO transfers (from_account, to_account, amount) VALUES ('${fromAccount}', '${toAccount}', ${amount})`
    );
    
    // โœ… Commit if all operations succeed
    const committed = commitTransaction(transaction);
    const summary = getTransactionSummary(committed);
    
    console.log(`๐Ÿ’ฐ Transfer completed in ${summary.duration}ms with ${summary.operationCount} operations`);
    
  } catch (error) {
    // ๐Ÿ”„ Rollback on error
    if (canRollback(transaction)) {
      const rolledBack = rollbackTransaction(transaction);
      const summary = getTransactionSummary(rolledBack);
      console.log(`๐Ÿ”„ Transaction rolled back after ${summary.duration}ms`);
    } else {
      const failed = failTransaction(transaction, error as Error);
      const summary = getTransactionSummary(failed);
      console.log(`โŒ Transaction failed after ${summary.duration}ms`);
    }
    
    throw error;
  }
}

// โŒ These would be compile-time errors:
// commitTransaction(beginTransaction());          // โŒ Must activate first
// executeOperation(beginTransaction(), "SQL");   // โŒ Must be active to execute
// rollbackTransaction(commitTransaction(active)); // โŒ Can't rollback committed transaction

๐Ÿ› ๏ธ Advanced Phantom Type Patterns

๐ŸŽฏ Capability-Based Security

// ๐Ÿ”’ Security capabilities as phantom types
type Capability = 'Read' | 'Write' | 'Delete' | 'Admin';

type SecureResource<TData, TCaps extends Capability[]> = {
  readonly data: TData;
  readonly owner: string;
  readonly createdAt: Date;
} & { readonly __capabilities: TCaps };

// ๐ŸŽจ Capability helpers
type HasCapability<TCaps extends Capability[], TCap extends Capability> = TCap extends TCaps[number] ? true : false;

type RequireCapability<TCaps extends Capability[], TCap extends Capability> = 
  HasCapability<TCaps, TCap> extends true ? TCaps : never;

// ๐Ÿ—๏ธ Resource constructors
function createResource<TData>(
  data: TData,
  owner: string
): SecureResource<TData, ['Read']> {
  return {
    data,
    owner,
    createdAt: new Date(),
    __capabilities: ['Read'] as const
  };
}

function grantCapability<TData, TCaps extends Capability[], TNewCap extends Capability>(
  resource: SecureResource<TData, TCaps>,
  capability: TNewCap
): SecureResource<TData, [...TCaps, TNewCap]> {
  return {
    ...resource,
    __capabilities: [...resource.__capabilities, capability] as [...TCaps, TNewCap]
  };
}

// ๐Ÿ›ก๏ธ Capability-protected operations
function readResource<TData, TCaps extends Capability[]>(
  resource: SecureResource<TData, RequireCapability<TCaps, 'Read'>>
): TData {
  console.log("๐Ÿ“– Reading resource data");
  return resource.data;
}

function writeResource<TData, TCaps extends Capability[]>(
  resource: SecureResource<TData, RequireCapability<TCaps, 'Write'>>,
  newData: TData
): SecureResource<TData, TCaps> {
  console.log("โœ๏ธ Writing resource data");
  return {
    ...resource,
    data: newData
  };
}

function deleteResource<TData, TCaps extends Capability[]>(
  resource: SecureResource<TData, RequireCapability<TCaps, 'Delete'>>
): void {
  console.log("๐Ÿ—‘๏ธ Deleting resource");
  // Resource deletion logic
}

function administerResource<TData, TCaps extends Capability[]>(
  resource: SecureResource<TData, RequireCapability<TCaps, 'Admin'>>
): void {
  console.log("๐Ÿ”ง Performing admin operations");
  // Admin operations
}

// ๐Ÿงช Capability-based security example
interface DocumentData {
  title: string;
  content: string;
  version: number;
}

const document = createResource<DocumentData>(
  { title: "Secret Document", content: "Classified information", version: 1 },
  "user123"
);

// โœ… Can read (has Read capability)
const content = readResource(document);
console.log(`๐Ÿ“„ Document: ${content.title}`);

// ๐ŸŽฏ Grant additional capabilities
const writableDoc = grantCapability(document, 'Write');
const deletableDoc = grantCapability(writableDoc, 'Delete');
const adminDoc = grantCapability(deletableDoc, 'Admin');

// โœ… Now can perform all operations
const updatedDoc = writeResource(adminDoc, {
  title: "Updated Secret Document",
  content: "Updated classified information", 
  version: 2
});

administerResource(adminDoc);
// deleteResource(adminDoc); // Would delete the resource

// โŒ These would be compile-time errors:
// writeResource(document, newData);    // โŒ No Write capability
// deleteResource(writableDoc);         // โŒ No Delete capability  
// administerResource(deletableDoc);    // โŒ No Admin capability

๐Ÿ“ Dimensional Analysis

// ๐Ÿ“ Physical dimensions as phantom types
type Dimension = {
  length: number;
  mass: number;
  time: number;
  current: number;
  temperature: number;
  amount: number;
  luminous: number;
};

type Quantity<D extends Dimension> = number & { readonly __dimension: D };

// ๐ŸŽฏ Basic SI units
type Dimensionless = Quantity<{ length: 0; mass: 0; time: 0; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Length = Quantity<{ length: 1; mass: 0; time: 0; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Mass = Quantity<{ length: 0; mass: 1; time: 0; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Time = Quantity<{ length: 0; mass: 0; time: 1; current: 0; temperature: 0; amount: 0; luminous: 0 }>;

// ๐Ÿ”„ Derived units
type Area = Quantity<{ length: 2; mass: 0; time: 0; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Volume = Quantity<{ length: 3; mass: 0; time: 0; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Velocity = Quantity<{ length: 1; mass: 0; time: -1; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Acceleration = Quantity<{ length: 1; mass: 0; time: -2; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Force = Quantity<{ length: 1; mass: 1; time: -2; current: 0; temperature: 0; amount: 0; luminous: 0 }>;
type Energy = Quantity<{ length: 2; mass: 1; time: -2; current: 0; temperature: 0; amount: 0; luminous: 0 }>;

// ๐Ÿ—๏ธ Unit constructors
const meter = (value: number): Length => value as Length;
const kilogram = (value: number): Mass => value as Mass;
const second = (value: number): Time => value as Time;
const dimensionless = (value: number): Dimensionless => value as Dimensionless;

// ๐Ÿงฎ Dimensional arithmetic types
type AddDimensions<D1 extends Dimension, D2 extends Dimension> = {
  length: D1['length'] + D2['length'];
  mass: D1['mass'] + D2['mass'];
  time: D1['time'] + D2['time'];
  current: D1['current'] + D2['current'];
  temperature: D1['temperature'] + D2['temperature'];
  amount: D1['amount'] + D2['amount'];
  luminous: D1['luminous'] + D2['luminous'];
};

type SubtractDimensions<D1 extends Dimension, D2 extends Dimension> = {
  length: D1['length'] extends number ? D2['length'] extends number ? 
    D1['length'] - D2['length'] extends number ? D1['length'] - D2['length'] : never : never : never;
  mass: D1['mass'] extends number ? D2['mass'] extends number ? 
    D1['mass'] - D2['mass'] extends number ? D1['mass'] - D2['mass'] : never : never : never;
  time: D1['time'] extends number ? D2['time'] extends number ? 
    D1['time'] - D2['time'] extends number ? D1['time'] - D2['time'] : never : never : never;
  current: D1['current'] - D2['current'];
  temperature: D1['temperature'] - D2['temperature'];
  amount: D1['amount'] - D2['amount'];
  luminous: D1['luminous'] - D2['luminous'];
};

// โœจ Dimensionally-safe operations
function multiply<D1 extends Dimension, D2 extends Dimension>(
  a: Quantity<D1>,
  b: Quantity<D2>
): Quantity<AddDimensions<D1, D2>> {
  return ((a as number) * (b as number)) as Quantity<AddDimensions<D1, D2>>;
}

function divide<D1 extends Dimension, D2 extends Dimension>(
  a: Quantity<D1>,
  b: Quantity<D2>
): Quantity<SubtractDimensions<D1, D2>> {
  return ((a as number) / (b as number)) as any;
}

function add<D extends Dimension>(a: Quantity<D>, b: Quantity<D>): Quantity<D> {
  return ((a as number) + (b as number)) as Quantity<D>;
}

function square<D extends Dimension>(a: Quantity<D>): Quantity<AddDimensions<D, D>> {
  return multiply(a, a);
}

// ๐Ÿงช Physics calculations with dimensional safety
const distance = meter(100);        // 100 m
const time = second(10);           // 10 s
const mass = kilogram(5);          // 5 kg

// โœ… Dimensionally correct calculations
const velocity = divide(distance, time);              // m/s
const acceleration = divide(velocity, time);          // m/sยฒ
const force = multiply(mass, acceleration);           // kgโ‹…m/sยฒ (Newtons)
const area = square(distance);                        // mยฒ
const kineticEnergy = multiply(                       // kgโ‹…mยฒ/sยฒ (Joules)
  multiply(dimensionless(0.5), mass),
  square(velocity)
);

console.log(`๐Ÿš— Velocity: ${velocity} m/s`);
console.log(`โšก Acceleration: ${acceleration} m/sยฒ`);
console.log(`๐Ÿ’ช Force: ${force} N`);
console.log(`๐Ÿ“ Area: ${area} mยฒ`);
console.log(`โšก Kinetic Energy: ${kineticEnergy} J`);

// โŒ These would be compile-time errors - dimensional mismatches!
// const invalid1 = add(distance, time);        // โŒ Can't add length + time
// const invalid2 = divide(mass, area);         // โŒ Unusual dimension combination  
// const invalid3 = multiply(velocity, mass);   // โŒ Wrong dimensions for force

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered the ethereal art of phantom types! Hereโ€™s what you now command:

  • โœ… Phantom type fundamentals and zero-cost abstractions ๐Ÿ’ช
  • โœ… State machine modeling with compile-time state tracking ๐Ÿ›ก๏ธ
  • โœ… Unit system safety preventing dimensional errors ๐ŸŽฏ
  • โœ… Protocol enforcement ensuring correct API usage patterns ๐Ÿ›
  • โœ… Capability-based security with type-level permissions ๐Ÿš€
  • โœ… Advanced phantom patterns for domain modeling โœจ
  • โœ… Type-level constraint programming with phantom parameters ๐Ÿ”„

Remember: Phantom types are your invisible guardians, providing maximum safety with zero runtime cost! ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve unlocked the phantom dimension of TypeScriptโ€™s type system!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Apply phantom types to model state machines in your applications
  2. ๐Ÿ—๏ธ Create unit-safe APIs for scientific or engineering applications
  3. ๐Ÿ“š Move on to our next tutorial: Type-Level Arithmetic - Number Operations
  4. ๐ŸŒŸ Build capability-based security systems with phantom types
  5. ๐Ÿ” Explore phantom types in functional programming languages for inspiration
  6. ๐ŸŽฏ Design APIs that make invalid states unrepresentable at compile time

Remember: Phantom types are your secret weapon for encoding invariants and constraints directly into the type system! ๐Ÿš€


Happy phantom type mastering! ๐ŸŽ‰๐Ÿ‘ปโœจ