Type-Safe ID Types
Typo generates zero-overhead type wrappers for all schema types marked with format: id or similar patterns. This eliminates primitive obsession and provides compile-time safety.
The Problem
Standard OpenAPI generators produce code with strings everywhere:
// ❌ Primitive obsession - easy to mix up parameters
void getPet(String petId);
void getOwner(String ownerId);
// Oops! Wrong ID passed - compiles fine, fails at runtime
getPet(ownerId);
The Solution
Typo generates dedicated ID types that catch mistakes at compile time:
Java (record)
/** Unique pet identifier */
public record PetId(@JsonValue String value) {
@Override
public String toString() {
return value;
}
}
Kotlin (data class)
/** Unique pet identifier */
data class PetId @JsonCreator constructor(
@get:JsonValue val value: String
) {
override fun toString(): String = value
}
Scala (value class)
/** Unique pet identifier */
case class PetId(value: String) extends AnyVal
object PetId {
implicit val decoder: Decoder[PetId] = Decoder[String].map(PetId.apply)
implicit val encoder: Encoder[PetId] = Encoder[String].contramap(_.value)
/** Path extractor for Http4s routes */
def unapply(str: String): Option[PetId] = Some(PetId(str))
}
Usage in APIs
With type-safe IDs, the compiler prevents mixing up different ID types:
// ✅ Type-safe - compiler enforces correct ID types
void getPet(PetId petId);
void getOwner(OwnerId ownerId);
// ❌ Compile error! Cannot pass OwnerId where PetId is expected
getPet(ownerId);
This propagates through your entire codebase - from HTTP endpoints to database queries.