Skip to content

ADR-009: Unified Type System

Status

Proposed | Accepted | Superseded by | Deprecates

Version History

  • v1: 2025-05-02 - Initial proposal

Context

The current content system implementation has evolved multiple type structures across different layers, leading to inconsistencies, redundant transformations, and unclear boundaries between abstractions. Specific issues include:

  1. Interface Inconsistencies: Different interfaces for similar concepts (Content, RawContent, AdapterContent) with unclear relationships and conversion patterns.

  2. Data Type Handling: Inconsistent handling of content data, with some interfaces using string, others supporting binary data, and unclear transformation patterns between them.

  3. Metadata Structures: Different metadata structures across layers with repeated transformation and normalization.

  4. Environment-Specific Types: Different type structures for browser, Node.js, and other environments without a clear mapping between them.

  5. Transformation Patterns: Ad-hoc transformations between type structures, making it difficult to reason about data flow through the system.

These inconsistencies make the system harder to understand, use, and extend. They also create unnecessary type conversion overhead and potential bugs at the boundaries between abstractions.

Decision

We will implement a unified type system based on generic types and explicit transformations:

  1. Generic Content Interface:

    typescript
    interface Content<T = string> {
      data: T
      contentType: string
      metadata: ContentMetadata
    }
  2. Specialized Content Types:

    typescript
    type TextContent = Content<string>
    type BinaryContent = Content<Uint8Array>
    type JsonContent = Content<Record<string, unknown>>
    type MdxContent = Content<{
      code: string
      frontmatter: Record<string, unknown>
      compiledSource?: string
    }>
  3. Standardized Metadata Interface:

    typescript
    interface ContentMetadata {
      // Core fields
      uri: string
      createdAt: Date
      updatedAt: Date
    
      // Optional fields with explicit types
      title?: string
      description?: string
      tags?: string[]
    
      // Extension mechanism
      [key: string]: unknown
    }
  4. Explicit Transformation Functions:

    typescript
    // Type-safe transformations
    function transformContent<T, U>(
      content: Content<T>,
      transform: (data: T) => U
    ): Content<U> {
      return {
        ...content,
        data: transform(content.data),
      }
    }
    
    // Common transformers
    const stringifyContent = <T>(content: Content<T>): TextContent =>
      transformContent(content, data => JSON.stringify(data))
    
    const parseJsonContent = (content: TextContent): JsonContent =>
      transformContent(content, data => JSON.parse(data))
  5. Environment-Specific Adapters:

    typescript
    // Define storage mechanisms with specific data requirements
    interface StorageAdapter<T = string> {
      read(uri: string): Promise<Content<T>>
      write(uri: string, content: Content<T>): Promise<void>
      delete(uri: string): Promise<void>
      list(query?: ContentQuery): Promise<string[]>
    }

Alternatives Considered

Option 1: Class-Based Type Hierarchy

  • Pros: Familiar object-oriented approach, clear inheritance relationships
  • Cons: Less flexible than composition, harder to integrate with functional patterns
  • Outcome: Rejected as inconsistent with the compositional architecture (ADR-007)

Option 2: Union Types with Type Guards

  • Pros: Maintains distinct types without generics, explicit type checking
  • Cons: More runtime type checking, harder to compose, more verbose
  • Outcome: Partially adopted for validation scenarios, but not as the primary approach

Option 3: Protocol-Based Type System

  • Pros: Focuses on behavior rather than structure, potentially more flexible
  • Cons: TypeScript has limited support for structural typing, more complex
  • Outcome: Rejected due to increased complexity and limited TypeScript support

Option 4: Keep Current Approach with Better Documentation

  • Pros: Minimal changes to existing code, low implementation effort
  • Cons: Doesn’t address fundamental inconsistencies, technical debt remains
  • Outcome: Rejected as it doesn’t solve the underlying problems

Consequences

Benefits

  1. Type Safety: Generic types provide compile-time safety for different content types
  2. Consistency: Single Content interface across all layers with clear specialization
  3. Composability: Transformation functions integrate well with functional composition
  4. Flexibility: Generic types support both simple and complex content structures
  5. Clarity: Explicit transformations make data flow through the system more apparent

Challenges

  1. Migration Effort: Existing code needs to be updated to use the new type system
  2. Learning Curve: Generic types may be less familiar to some developers
  3. Performance: Type transformations may introduce slight overhead (though this should be minimal)
  4. Compatibility: External integrations may need adaptation layers

Implementation

The implementation will proceed in the following phases:

Phase 1: Core Type Definitions

  1. Create new unified type definitions in /src/lib/content/types.ts
  2. Define transformation utilities in /src/lib/content/transformers.ts
  3. Update content store interface to use generic types
  4. Update adapter interfaces to specify data requirements

Phase 2: Adapter Updates

  1. Update memory adapter to use the generic Content interface
  2. Update filesystem adapter to standardize on Content<string>
  3. Update browser adapters to use appropriate Content specializations
  4. Create adapter utilities for common transformations

Phase 3: Middleware Updates

  1. Update validation middleware to use generic Content
  2. Update caching middleware for consistent type handling
  3. Update transforming middleware to use explicit transformation functions
  4. Create middleware utilities for type-safe composition

Phase 4: React Integration Updates

  1. Update content hooks to leverage generic types
  2. Create specialized hooks for common content types
  3. Update components to handle different content types consistently
  4. Create examples demonstrating type-safe content handling

Documentation Updates

The following documentation will need to be created or updated:

  1. New Documentation:

    • reference/types/content-types.md: Comprehensive documentation of the new type system
    • guides/migration/type-system-migration.md: Guide for migrating to the new type system
    • concepts/content-model/content-structure.md: Conceptual explanation of content model
  2. Updated Documentation:

    • Update adapter documentation with type specialization information
    • Update React integration guides with generic type usage
    • Update middleware documentation with type transformation patterns

References

Released under the MIT License.