Effect CommunityEC
Effect Community2w ago
21 replies
kristo

Handling STI with Effect Schema in TypeScript

I have a database using STI (Single Table Inheritance) for polymorphism. It returns a wide polymorphic type where variant-specific fields are nullable. Effect Schema discriminated unions expect a narrow type per variant.
- Schema.decode requires exact Encoded type match (fails at compile-time)
- Schema.decodeUnknown works but loses compile-time safety
Question: Is there a pattern to create a schema with a wider Encoded type that narrows to a specific variant?

  import { Schema } from "effect"

  // Domain schemas (narrow - what we want)
  const StageRace = Schema.Struct({
    _tag: Schema.Literal("StageRace"),
    id: Schema.String,
    date: Schema.String,
    enddate: Schema.String, // Only StageRace has this
  })

  const OneDayRace = Schema.Struct({
    _tag: Schema.Literal("OneDayRace"),
    id: Schema.String,
    date: Schema.String,
  })

  // What the database returns (wide - STI polymorphism)
  type DatabaseRow = {
    _tag: "StageRace" | "OneDayRace"
    id: string
    date: string
    enddate: string | null // Nullable because OneDayRace doesn't have it
  }

  declare const row: DatabaseRow

  // ❌ Schema.decode fails - DatabaseRow doesn't match StageRace.Encoded, it might not have enddate
  Schema.decode(StageRace)(row)

  // ✅ Works at runtime, but loses compile-time input type checking
  Schema.decodeUnknown(StageRace)(row)

  // What we want: A schema where
  // - Encoded = DatabaseRow (wide, accepts DB polymorphism)
  // - Type = StageRace (narrow, validated domain type)
  // - Schema.decode(schema)(row) type-checks that row at least has id, date
  //   AND validates at runtime
Was this page helpful?