Skip to main content

TypeDefinition

TypeDefinition is a sealed trait that represents any Scala type definition—case classes, sealed traits, enums, objects, newtypes, type aliases, abstract classes, and regular traits. It's the foundation for modeling Scala's type system in the IR.

Use Cases

  • As the common interface for all compound types when building a ScalaFile
  • When you need to handle multiple type kinds uniformly (e.g., iterate over all types in a file)
  • As a base for specific type definitions like CaseClass, SealedTrait, Enum, etc.

Variants

TypeDefinition has 9 concrete implementations:

TypePurposeScala Feature
CaseClassProduct types with fieldsScala 2 & 3
SealedTraitSum types (ADTs) with casesScala 2 & 3
TraitOpen trait definitionsScala 2 & 3
AbstractClassAbstract class definitionsScala 2 & 3
EnumScala 3 enum definitionsScala 3 only
ObjectDefSingleton objectsScala 2 & 3
OpaqueTypeOpaque type aliasesScala 3 (Scala 2 fallback to type alias)
NewtypeZero-cost wrappersScala 2 & 3
TypeAliasType aliases (with optional type parameters)Scala 2 & 3

Common Interface

Every TypeDefinition provides:

sealed trait TypeDefinition {
def name: String // The name of the type
def annotations: List[Annotation] // Annotations applied to it
def doc: Option[String] // Optional documentation
}

Choosing the Right Variant

Choose the right TypeDefinition for your use case:

Data Structures

Case Class — Immutable record with named fields and automatic equals, hashCode, copy:

import zio.blocks.codegen.ir._

val user = CaseClass(
name = "User",
fields = List(
Field("id", TypeRef.Long),
Field("name", TypeRef.String)
)
)

Newtype — Zero-cost wrapper for a single underlying type (e.g., UserId wrapping Long):

import zio.blocks.codegen.ir._

val userId = Newtype("UserId", TypeRef.Long)

Type Alias — Synonym for an existing type:

import zio.blocks.codegen.ir._

val stringId = TypeAlias("StringId", typeRef = TypeRef.String)

Type Alias with Type Parameters — Generic type alias:

import zio.blocks.codegen.ir._

val pair = TypeAlias(
"Pair",
typeRef = TypeRef("Tuple2", List(TypeRef("A"), TypeRef("B"))),
typeParams = List(TypeParam("A"), TypeParam("B"))
)

Sum Types (ADTs)

Sealed Trait — Discriminated union type with cases:

import zio.blocks.codegen.ir._

val payment = SealedTrait(
name = "Payment",
cases = List(
SealedTraitCase.CaseClassCase(
CaseClass("Card", List(Field("number", TypeRef.String)))
),
SealedTraitCase.CaseObjectCase("Cash")
)
)

Enum (Scala 3 only) — Enumeration with simple or parameterized cases:

import zio.blocks.codegen.ir._

val color = Enum(
name = "Color",
cases = List(
EnumCase.SimpleCase("Red"),
EnumCase.SimpleCase("Green"),
EnumCase.ParameterizedCase("Custom", List(Field("rgb", TypeRef.Int)))
)
)

Abstractions

Trait — Open trait for behavior and contracts:

import zio.blocks.codegen.ir._

val comparable = Trait(
name = "Comparable",
members = List(
ObjectMember.DefMember(
Method("compare", params = Nil, returnType = TypeRef.Int)
)
)
)

Abstract Class — Mix of fields and methods, but not instantiable:

import zio.blocks.codegen.ir._

val entity = AbstractClass(
name = "Entity",
fields = List(Field("id", TypeRef.Long))
)

Object — Singleton object with static-like members:

import zio.blocks.codegen.ir._

val utils = ObjectDef(
name = "Utils",
members = List(
ObjectMember.ValMember("Version", TypeRef.String, "\"1.0.0\"")
)
)

Type Aliases (Advanced)

Opaque Type (Scala 3) — Type-safe wrapper with an underlying type (distinct from Newtype):

import zio.blocks.codegen.ir._

val userId2 = OpaqueType("UserId", underlyingType = TypeRef.Long)

Common Operations

All TypeDefinition variants support these core operations:

Accessing Components

import zio.blocks.codegen.ir._

val cc = CaseClass("User", List(Field("id", TypeRef.Long)))

cc.name // "User"
cc.annotations // List[Annotation] (empty by default)
cc.doc // Option[String] (documentation)

With Annotations

Add annotations (like @deprecated or custom annotations):

import zio.blocks.codegen.ir._

val annotated = CaseClass(
name = "OldAPI",
fields = Nil,
annotations = List(
Annotation("deprecated")
)
)

With Documentation

Include scaladoc or javadoc comments:

import zio.blocks.codegen.ir._

val documented = CaseClass(
name = "User",
fields = List(Field("id", TypeRef.Long)),
doc = Some("Represents a user in the system")
)

With Derives

Add derives clauses for automatic typeclass derivation:

import zio.blocks.codegen.ir._

val derived = CaseClass(
name = "Point",
fields = List(
Field("x", TypeRef.Int),
Field("y", TypeRef.Int)
),
derives = List("Show", "Eq")
)

Examples

Practical examples demonstrate common usage:

Example 1: Combining Types in a File

A file with multiple TypeDefinition variants:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val file = ScalaFile(
packageDecl = PackageDecl("com.example"),
types = List(
// Case class
CaseClass("Person", List(Field("name", TypeRef.String))),

// Sealed trait (sum type)
SealedTrait(
"Result",
typeParams = List(TypeParam("T")),
cases = List(
SealedTraitCase.CaseClassCase(
CaseClass("Success", List(Field("value", TypeRef("T"))), typeParams = List(TypeParam("T")))
),
SealedTraitCase.CaseObjectCase("Failure")
)
),

// Object with static members
ObjectDef(
"Config",
members = List(
ObjectMember.ValMember("MaxRetries", TypeRef.Int, "3")
)
)
)
)

Emits:

import zio.blocks.codegen.emit._
ScalaEmitter.emit(file, EmitterConfig())
// res15: String = """package com.example
//
// case class Person(
// name: String,
// )
//
// sealed trait Result[T]
//
// object Result {
// case class Success[T](
// value: T,
// ) extends Result[T]
// case object Failure extends Result[T]
// }
//
// object Config {
// val MaxRetries: Int = 3
// }
// """

Example 2: Type Parameters and Generic Types

Defining a polymorphic case class:

import zio.blocks.codegen.ir._
import zio.blocks.codegen.emit._

val box = CaseClass(
name = "Box",
fields = List(
Field("value", TypeRef("A"))
),
typeParams = List(
TypeParam("A")
)
)

val file = ScalaFile(
packageDecl = PackageDecl("com.example"),
types = List(box)
)

Emits:

import zio.blocks.codegen.emit._
ScalaEmitter.emit(file, EmitterConfig())
// res17: String = """package com.example
//
// case class Box[A](
// value: A,
// )
// """