Skip to main content
Sonamu automatically generates TypeScript types that provide complete type safety based on Entity definitions. This document explains how each Entity field type is converted to TypeScript types.

Type Conversion Overview

Field Types

Entity Prop → TypeScript Type auto-conversion and type safety

Nullable Support

nullable: true → T | null optional null handling

Array Types

type[] → T[] array type auto-generation

Relation Types

BelongsToOne → number (FK) relation field type conversion

Basic Type Conversion

Shows how each Entity field type is converted to TypeScript types.

Numeric Types

{
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "view_count", "type": "integer", "nullable": true },
    { "name": "user_id", "type": "bigInteger" }
  ]
}
Entity TypeTypeScript TypeDescription
integernumber32bit integer
integer[]number[]Integer array
bigIntegerbigint64bit integer (large numbers)
bigInteger[]bigint[]Large integer array
bigInteger vs integer: integer is sufficient for IDs or counts, but use bigInteger for timestamps (milliseconds) or very large numbers.

String Types

{
  "props": [
    { "name": "email", "type": "string", "length": 100 },
    { "name": "bio", "type": "string" },
    { "name": "tags", "type": "string[]" }
  ]
}
Entity TypeTypeScript TypeDescription
stringstringString
string[]string[]String array
The length attribute doesn’t affect TypeScript types, but is converted to .max(length) validation in Zod.

Boolean Types

{
  "props": [
    { "name": "is_active", "type": "boolean" },
    { "name": "permissions", "type": "boolean[]" }
  ]
}

Date Types

{
  "props": [
    { "name": "created_at", "type": "date" },
    { "name": "login_history", "type": "date[]", "nullable": true }
  ]
}
JSON Serialization: In API responses, Date is transmitted as an ISO string (string). Zod’s z.date() automatically converts the string to a Date object.
// API Response (JSON)
{ "created_at": "2024-01-01T00:00:00.000Z" }

// After Zod parsing
{ created_at: Date }  // Automatically converted to Date object

UUID Types

{
  "props": [
    { "name": "uuid", "type": "uuid" },
    { "name": "related_ids", "type": "uuid[]" }
  ]
}
TypeScript doesn’t have a dedicated UUID type, so it’s represented as string. Format is validated with .uuid() in Zod validation.

Numeric Precision Types

Two types for handling PostgreSQL high-precision numbers.

number vs numeric

{
  "props": [
    { 
      "name": "price", 
      "type": "number",
      "precision": 10,
      "scale": 2
    },
    { 
      "name": "balance", 
      "type": "numeric",
      "precision": 20,
      "scale": 8
    }
  ]
}
Entity TypeTypeScript TypePostgreSQL TypeUse Case
numbernumberreal, double precision, numericGeneral calculations (floating point)
numericstringnumericFinancial calculations (fixed point)
Choosing number vs numeric:Use number when:
  • General numeric calculations
  • Decimal precision is not critical
  • Scientific calculations, statistics
Use numeric when:
  • Financial calculations (money, prices, balances)
  • Exact decimals are required
  • Cryptocurrency amounts
// number - floating point errors possible
0.1 + 0.2 === 0.3; // false (0.30000000000000004)

// numeric - exact value preserved as string
import Decimal from "decimal.js";
new Decimal("0.1").plus("0.2").equals("0.3"); // true

Enum Types

Entity Enums are converted to TypeScript Union types.
{
  "enums": {
    "UserRole": {
      "admin": "Administrator",
      "moderator": "Moderator",
      "normal": "Normal User"
    }
  },
  "props": [
    { "name": "role", "type": "enum", "id": "UserRole" },
    { "name": "badges", "type": "enum[]", "id": "UserBadge" }
  ]
}
Benefits:
  • Autocomplete support
  • Type safety guaranteed
  • Compile errors for invalid values
// ✅ Correct usage
const user: User = {
  role: "admin", // Autocompleted
};

// ❌ Compile error
const user: User = {
  role: "guest", // Error: Type '"guest"' is not assignable
};

JSON Type

Used when storing complex object structures as JSON.
{
  "props": [
    { "name": "metadata", "type": "json", "id": "PostMetadata" }
  ]
}
The json type references the Zod schema specified by id. This schema must be defined directly in {entity}.types.ts.

Virtual Type

Computed fields not stored in the database.
{
  "props": [
    { "name": "full_name", "type": "virtual", "id": "UserFullName" }
  ]
}
Virtual fields are computed in Model’s Enhancer:
const enhancers = this.createEnhancers({
  A: (row) => ({
    ...row,
    full_name: `${row.first_name} ${row.last_name}`,
  }),
});

Vector Type

Type for vector search (pgvector).
{
  "props": [
    { "name": "embedding", "type": "vector", "dimensions": 1536 },
    { "name": "embeddings_history", "type": "vector[]", "dimensions": 1536 }
  ]
}
dimensions doesn’t affect TypeScript types, but is used in PostgreSQL schema and Zod validation.
// Zod validation
z.array(z.number()).length(1536); // dimensions validation

Relation Types

Fields representing relationships between Entities.

BelongsToOne (Many-to-One)

{
  "props": [
    {
      "name": "user",
      "type": "relation",
      "relationType": "BelongsToOne",
      "with": "User"
    }
  ]
}
Field name transformation: useruser_idIn Entity, you define as user, but the actual TypeScript type becomes user_id.

OneToOne (One-to-One)

{
  "props": [
    {
      "name": "profile",
      "type": "relation",
      "relationType": "OneToOne",
      "with": "Profile",
      "hasJoinColumn": true
    }
  ]
}

HasMany / ManyToMany

{
  "props": [
    {
      "name": "posts",
      "type": "relation",
      "relationType": "HasMany",
      "with": "Post",
      "joinColumn": "user_id"
    }
  ]
}
HasMany and ManyToMany are not included in base types. When you define a Subset, those types are included in the Subset type.

Nullable Handling

nullable: true is converted to TypeScript Union type.
{
  "props": [
    { "name": "nickname", "type": "string", "nullable": true },
    { "name": "tags", "type": "string[]", "nullable": true },
    {
      "name": "manager",
      "type": "relation",
      "relationType": "BelongsToOne",
      "with": "User",
      "nullable": true
    }
  ]
}
Array nullable: nullable: true for string[] means the array itself can be null.
// nullable: true
tags: string[] | null

// ✅ Possible
const user = { tags: null };
const user = { tags: [] };
const user = { tags: ["a", "b"] };

// ❌ Not possible
const user = { tags: undefined };

// If array elements should be nullable, you need a custom type
tags: (string | null)[]  // Define as custom type

Complete Type Mapping Table

Mapping table of all Entity Prop types to TypeScript types.
Entity TypeTypeScript TypeNotes
integernumber32bit integer
integer[]number[]Integer array
bigIntegerbigint64bit integer
bigInteger[]bigint[]Large integer array
stringstringString
string[]string[]String array
numbernumberFloating point
number[]number[]Floating point array
numericstringFixed point (precision as string)
numeric[]string[]Fixed point array
booleanbooleanBoolean
boolean[]boolean[]Boolean array
dateDateDate (string in JSON)
date[]Date[]Date array
uuidstringUUID string
uuid[]string[]UUID array
enumEnumTypeUnion type
enum[]EnumType[]Union type array
jsonCustomTypeCustom object type
virtualCustomTypeComputed field
vectornumber[]Vector (pgvector)
vector[]number[][]Vector array
tsvectorstringFull-text search (PostgreSQL)
BelongsToOnenumberForeign Key (FK)
OneToOne (hasJoinColumn)numberForeign Key (FK)
HasMany-Not in Base type
ManyToMany-Not in Base type

Practical Example

Type conversion example for an actual User Entity.
{
  "id": "User",
  "table": "users",
  "props": [
    { "name": "id", "type": "integer" },
    { "name": "email", "type": "string", "length": 100 },
    { "name": "username", "type": "string", "length": 50 },
    { "name": "role", "type": "enum", "id": "UserRole" },
    { "name": "is_active", "type": "boolean" },
    { "name": "bio", "type": "string", "nullable": true },
    { "name": "follower_count", "type": "integer" },
    { "name": "created_at", "type": "date" },
    { "name": "updated_at", "type": "date" },
    {
      "name": "profile",
      "type": "relation",
      "relationType": "OneToOne",
      "with": "Profile",
      "hasJoinColumn": true,
      "nullable": true
    }
  ],
  "enums": {
    "UserRole": {
      "admin": "Administrator",
      "moderator": "Moderator",
      "normal": "Normal User"
    }
  }
}

Next Steps