Skip to content

TypeScript Types

Monch provides comprehensive TypeScript support with full type inference from your schemas.

Type Inference

Types flow automatically from your Zod schema:

typescript
const Users = collection({
  name: 'users',
  schema: {
    _id: field.id(),
    name: field.string(),
    email: field.email(),
    role: field.enum(['user', 'admin']).default('user'),
  },
  timestamps: true,
});

// TypeScript automatically infers:
// - Document type includes all fields + timestamps
// - Input type respects defaults/optionals
// - Query results are typed
// - Serialized output is typed

Extracting Types from Collections

The recommended way to get types:

typescript
import { collection, field, type ModelOf, type SerializedOf } from '@codician-team/monch';

export const Users = collection({
  name: 'users',
  schema: {
    _id: field.id(),
    name: field.string(),
    email: field.email(),
  },
  timestamps: true,
});

// Extract types from the collection (includes timestamps!)
export type User = ModelOf<typeof Users>;
// { _id: ObjectId; name: string; email: string; createdAt: Date; updatedAt: Date }

export type SerializedUser = SerializedOf<typeof Users>;
// { _id: string; name: string; email: string; createdAt: string; updatedAt: string }

Type Extraction Helpers

Type HelperDescription
ModelOf<C>Extract document type from collection (includes timestamps)
SerializedOf<C>Extract serialized type from collection
SchemaOf<C>Extract the raw schema type from collection

Schema Types

Build types from schemas directly:

typescript
import { type Doc, type Input, type Serialized } from '@codician-team/monch';

const userSchema = {
  _id: field.id(),
  name: field.string(),
  email: field.email(),
};

// Document type (what's in MongoDB)
type User = Doc<typeof userSchema>;
// { _id: ObjectId; name: string; email: string }

// Input type (what you pass to insertOne)
type UserInput = Input<typeof userSchema>;
// { _id?: ObjectId | string; name: string; email: string }

// Serialized type (after .serialize())
type SerializedUser = Serialized<User>;
// { _id: string; name: string; email: string }

Adding Timestamps Manually

typescript
import { type WithTimestamps } from '@codician-team/monch';

type User = Doc<typeof userSchema>;
type UserWithTimestamps = WithTimestamps<User>;
// { _id: ObjectId; name: string; email: string; createdAt: Date; updatedAt: Date }

MonchFilter Type

For filter parameters that need to work with Zod-inferred types:

typescript
import { type MonchFilter } from '@codician-team/monch';

// MonchFilter is more permissive than MongoDB's Filter type
function findUsers(filter: MonchFilter<User>) {
  return Users.find(filter).toArray();
}

// All of these work without type errors:
findUsers({ email: 'test@example.com' });
findUsers({ createdAt: { $gte: new Date() } });
findUsers({ 'profile.name': 'John' });
findUsers({ $or: [{ status: 'active' }, { role: 'admin' }] });

All collection methods use MonchFilter internally, so you typically don't need to import it directly.

Importing Types

typescript
import {
  // Schema types
  type Doc,          // Document type from schema
  type Input,        // Input type (respects defaults/optionals)
  type Schema,       // Schema definition type

  // Operation types
  type Filter,       // MongoDB filter type (strict)
  type UpdateFilter, // MongoDB update type
  type MonchFilter,  // Relaxed filter type (recommended)

  // Serialization
  type Serialized,   // BSON -> plain JS conversion

  // Type extraction (from collections)
  type ModelOf,      // Extract document type from collection
  type SerializedOf, // Extract serialized type from collection
  type SchemaOf,     // Extract schema type from collection

  // Utilities
  type WithTimestamps, // Add createdAt/updatedAt to type
} from '@codician-team/monch';

Using Types in Components

typescript
// lib/models/user.model.ts
import { collection, field, type ModelOf, type SerializedOf } from '@codician-team/monch';

export const Users = collection({
  name: 'users',
  schema: {
    _id: field.id(),
    name: field.string(),
    email: field.email(),
    avatar: field.url().optional(),
  },
  timestamps: true,
});

export type User = ModelOf<typeof Users>;
export type SerializedUser = SerializedOf<typeof Users>;
typescript
// components/UserCard.tsx
import type { SerializedUser } from '@/lib/models/user.model';

interface UserCardProps {
  user: SerializedUser;
}

export function UserCard({ user }: UserCardProps) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      {user.avatar && <img src={user.avatar} alt={user.name} />}
      <small>Joined {new Date(user.createdAt).toLocaleDateString()}</small>
    </div>
  );
}

Type-Safe Server Actions

typescript
// app/actions/user.actions.ts
'use server';

import { Users, type User, type SerializedUser } from '@/lib/models';

export async function getUser(id: string): Promise<SerializedUser | null> {
  const user = await Users.findById(id);
  return user?.serialize() ?? null;
}

export async function updateUser(
  id: string,
  data: Partial<Pick<User, 'name' | 'avatar'>>
): Promise<SerializedUser | null> {
  const user = await Users.updateOne(
    { _id: id },
    { $set: data }
  );
  return user?.serialize() ?? null;
}

Generic Functions

Create type-safe generic functions:

typescript
import { type Collection, type ModelOf } from '@codician-team/monch';

async function findOrCreate<C extends Collection<any, any, any>>(
  collection: C,
  filter: object,
  defaults: Partial<ModelOf<C>>
): Promise<ModelOf<C>> {
  const existing = await collection.findOne(filter);
  if (existing) return existing;

  return collection.insertOne({
    ...defaults,
    ...filter,
  } as any);
}

// Usage
const user = await findOrCreate(Users, { email: 'alice@example.com' }, {
  name: 'Alice',
});

Runtime Schema Access

Access the Zod schema at runtime for validation or introspection:

typescript
const Users = collection({
  name: 'users',
  schema: {
    _id: field.id(),
    name: field.string(),
    email: field.email(),
  },
});

// Access runtime schema
const schema = Users.$schema;

// Validate data manually
const result = schema.safeParse({ name: 'Alice', email: 'invalid' });
if (!result.success) {
  console.log(result.error.issues);
}

// Create derived schemas
const PartialUser = schema.partial();
const UserWithExtra = schema.extend({ extra: z.string() });

Collection Properties

PropertyTypeDescription
$schemaZodObjectRuntime-accessible Zod schema
$modeltype onlyDocument type (use with typeof)
$serializedtype onlySerialized document type (use with typeof)

Released under the MIT License.