Hone logo
Hone
Problems

Implementing a Generic Session Types Framework in TypeScript

This challenge focuses on creating a type-safe and flexible framework for managing different types of user sessions within a TypeScript application. A robust session management system is crucial for maintaining user state, authentication, and authorization across requests, and a type-safe approach significantly reduces runtime errors.

Problem Description

Your task is to design and implement a TypeScript framework for managing various session types. This framework should allow for defining distinct session structures (e.g., UserSession, AdminSession, GuestSession) and provide a mechanism to store, retrieve, and check the type of the currently active session in a type-safe manner.

Key Requirements:

  1. Define Multiple Session Types: You must be able to define different interfaces or types representing various session structures. Each session type should have its own specific properties.
  2. Generic Session Store: Create a generic SessionStore class that can hold sessions. This store should be able to store any type of session initially.
  3. Type-Safe Retrieval: Implement a method within SessionStore that allows retrieving a session, and critically, allows you to assert or check its type. This retrieval should be type-safe, meaning TypeScript should understand the type of the session after successful retrieval and type checking.
  4. Type Checking/Narrowing: Provide a way to check if the currently stored session conforms to a specific session type. This should enable TypeScript's type narrowing capabilities.
  5. No Runtime Type Assertions (where avoidable): Aim to leverage TypeScript's static typing as much as possible to avoid explicit as assertions at runtime for type checking.

Expected Behavior:

  • A session store should be able to hold one session at a time.
  • When retrieving a session, if its type is known, TypeScript should reflect that.
  • You should be able to safely check if the current session is a UserSession and, if so, access UserSession-specific properties without type errors.

Edge Cases to Consider:

  • What happens if the session store is empty when get is called?
  • How do you handle situations where you expect a specific session type, but a different one (or none) is present?

Examples

Example 1: Defining and Storing Different Session Types

// Define session types
interface UserSession {
  userId: string;
  username: string;
  roles: string[];
}

interface AdminSession {
  adminId: string;
  permissions: string[];
}

interface GuestSession {
  guestId: string;
}

// Create session store instance (initially empty)
const sessionStore = new SessionStore<UserSession | AdminSession | GuestSession>();

// Store a UserSession
const userSessionData: UserSession = { userId: '123', username: 'Alice', roles: ['user'] };
sessionStore.set(userSessionData);

// Retrieve and check type
const currentSession = sessionStore.get();

if (currentSession && typeof currentSession.userId === 'string') { // Simple check, but we want better type narrowing
  // How to make TS know this is a UserSession here?
  console.log(`User ID: ${currentSession.userId}`); // Potential TS error if not narrowed
}

Expected Output (Conceptual):

The code should compile without errors, and within a type guard that confirms it's a UserSession, currentSession.userId should be accessible and correctly typed as string.

Example 2: Type Guard for Session Verification

// Assume sessionStore from Example 1 is populated with a UserSession.

// Function to check if the session is an AdminSession
function isAdminSession(session: UserSession | AdminSession | GuestSession | null | undefined): session is AdminSession {
  return session !== null && session !== undefined && 'adminId' in session;
}

const retrievedSession = sessionStore.get(); // Assume it contains a UserSession for this example

if (isAdminSession(retrievedSession)) {
  console.log(`This is an admin session with ID: ${retrievedSession.adminId}`); // TS error if retrievedSession is not narrowed to AdminSession
} else {
  console.log('This is not an admin session.');
}

Expected Output (Conceptual):

The output should indicate "This is not an admin session." In a scenario where an AdminSession was stored, retrievedSession.adminId would be accessible and correctly typed.

Example 3: Handling Empty Store

interface SimpleSession {
  id: number;
}

const emptyStore = new SessionStore<SimpleSession>();

const session = emptyStore.get();

if (session) {
  console.log(`Session ID: ${session.id}`);
} else {
  console.log("No session found.");
}

Expected Output:

No session found.

Constraints

  • TypeScript Version: Use TypeScript 4.0 or later.
  • Immutability: Aim for immutability where practical within the SessionStore (e.g., set should replace the existing session).
  • No External Libraries: The solution should rely solely on native TypeScript features.
  • Clarity: The code should be well-commented and easy to understand.

Notes

  • Consider how to best represent the current active session type within the SessionStore. A union type of all possible session types is a good starting point.
  • Think about how to enable type narrowing after retrieving a session from the store. This is where your primary innovation will lie.
  • You might want to explore using discriminated unions or other advanced TypeScript patterns to facilitate type checking.
  • The goal is to leverage TypeScript's compile-time checks to build a robust and safe session management system.
Loading editor...
typescript