zanith

TypeScript Setup

How Zanith provides compile-time type safety without code generation — and why that matters.

Why this matters

A typo in a field name shouldn't be something you discover in production. It should be a red squiggle in your editor, caught before you even save the file. Most ORMs achieve this by generating thousands of lines of type definitions. Zanith does it with zero generation — using TypeScript's own inference system.

Without types

  • ×Typos discovered at runtime
  • ×Wrong types cause silent bugs
  • ×No autocomplete on field names
  • ×Refactoring breaks silently

With Zanith types

  • Typos caught at compile time
  • Wrong types are TS errors
  • Full autocomplete everywhere
  • Refactoring is safe

How it works — the inference chain

When you call m.string(), it returns a FieldBuilder<string>. When you chain .nullable(), it becomes FieldBuilder<string | null>. This phantom type flows through the entire chain — no files generated, no build step.

m.string()

FieldBuilder<string>

defineModel()

extracts types

createZanith()

maps to API

db.user

ModelAPI<User>

TStype inference in action
// Each builder carries its TypeScript type:
m.string() // → FieldBuilder<string>
m.int() // → FieldBuilder<number>
m.boolean() // → FieldBuilder<boolean>
m.datetime() // → FieldBuilder<Date>
m.bigint() // → FieldBuilder<bigint>
m.string().nullable() // → FieldBuilder<string | null>
m.json<MyType>() // → FieldBuilder<MyType>
 
// defineModel extracts the full type map:
const User = defineModel((m) => ({
fields: {
email: m.string(), // string
name: m.string().nullable(), // string | null
age: m.int(), // number
},
}));
 
// createZanith maps model names to typed APIs:
const db = await createZanith({ models: { User }, adapter });
 
// Result is fully typed:
const users = await db.user.findMany();
// users: Array<{ email: string, name: string | null, age: number }>

What TypeScript catches

With Zanith's type system, these common mistakes become impossible:

TScompile-time errors
// ✅ Correct — email is string, age is number
db.user.findMany({ where: { email: '[email protected]' } });
db.user.findMany({ where: { age: { gt: 18 } } });
 
// ❌ TS Error — 'emale' is not a field on User
db.user.findMany({ where: { emale: 'test' } });
 
// ❌ TS Error — age expects number filter, not string
db.user.findMany({ where: { age: { gt: 'eighteen' } } });
 
// ❌ TS Error — 'status' doesn't exist on this model
db.user.create({ data: { status: 'active' } });

Zanith requires strict: truefor full type inference. Without strict mode, phantom types won't propagate correctly.

TStsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

Next steps