Skip to main content
This covers common TypeScript type-related errors in Sonamu and how to resolve them.

Reserved Keywords Collision

Symptoms

class UserModelClass extends BaseModelClass {
  async delete(id: number) {
    // ❌
    // delete is a JavaScript reserved keyword
  }
}
Runtime error:
Unexpected token 'delete'
Or during Sonamu sync:
Error: Reserved keyword 'delete' cannot be used as method name

Cause

Used JavaScript/TypeScript reserved keywords as method or property names.

Solution

Avoid using reserved keywords or use different names:
// ❌ Using reserved keywords
async delete() { }
async switch() { }
async return() { }

// βœ… Use different names
async remove() { }
async del() { }
async toggle() { }
async getReturn() { }
72 reserved keywords validated by Sonamu:
const RESERVED_KEYWORDS = [
  "break",
  "case",
  "catch",
  "class",
  "const",
  "continue",
  "debugger",
  "default",
  "delete",
  "do",
  "else",
  "enum",
  "export",
  "extends",
  "false",
  "finally",
  "for",
  "function",
  "if",
  "import",
  "in",
  "instanceof",
  "new",
  "null",
  "return",
  "super",
  "switch",
  "this",
  "throw",
  "true",
  "try",
  "typeof",
  "var",
  "void",
  "while",
  "with",
  "yield",
  "let",
  "static",
  "implements",
  "interface",
  "package",
  "private",
  "protected",
  "public",
  "await",
  "abstract",
  "as",
  "asserts",
  "any",
  "async",
  "boolean",
  "constructor",
  "declare",
  "get",
  "infer",
  "is",
  "keyof",
  "module",
  "namespace",
  "never",
  "readonly",
  "require",
  "number",
  "object",
  "set",
  "string",
  "symbol",
  "type",
  "undefined",
  "unique",
  "unknown",
  "from",
  "of",
];

Type Inference Failure

Symptoms

const users = await UserModel.findMany();
// Type: any[]  ❌ Type not properly inferred

Causes

  1. Sonamu syncer didn’t run properly
  2. .generated file is outdated
  3. TypeScript server cache issue

Solutions

1. Re-run Syncer

pnpm sonamu sync

2. Restart TypeScript Server

VSCode:
Command Palette (Cmd+Shift+P)
> TypeScript: Restart TS Server

3. Check Generated Files

// src/application/sonamu.generated.ts
export type UserSave = {
  id?: number;
  email: string;
  name: string;
  // ...
};
If file is missing or outdated, re-run sync.

BaseModel Method Type Error

Symptoms

class UserModelClass extends BaseModelClass {
  async findByEmail(email: string) {
    return this.findOne({ email });
    // Error: Property 'findOne' does not exist on type 'UserModelClass'
  }
}

Cause

BaseModel’s auto-generated method types are not properly applied.

Solutions

1. Check entity.json

{
  "properties": {
    "id": { "type": "id" },
    "email": { "type": "string" },
    "name": { "type": "string" }
  }
}

2. Check Model Class Definition

class UserModelClass extends BaseModel<User, UserSave> {
  entityName = "User" as const;
  // ...
}

export const UserModel = new UserModelClass();

3. Regenerate Types with Syncer

pnpm sonamu sync

Union Type Error

Symptoms

type OrderStatus = "pending" | "processing" | "completed";

class OrderModelClass extends BaseModelClass {
  async updateStatus(id: number, status: string) {
    // ❌
    // status should be OrderStatus
  }
}
Type safety is not guaranteed.

Solution

Specify explicit type:
type OrderStatus = "pending" | "processing" | "completed";

class OrderModelClass extends BaseModelClass {
  async updateStatus(id: number, status: OrderStatus) {
    // βœ…
    return this.updateOne({ id }, { status });
  }
}

Zod Schema Type Mismatch

Symptoms

const UserSchema = z.object({
  email: z.string(),
  age: z.number()
});

@api()
async createUser(email: string, age: string) {  // ❌ age should be number
  // ...
}

Cause

API method parameter type doesn’t match Zod schema.

Solutions

1. Fix Parameter Type

@api()
async createUser(email: string, age: number) {  // βœ…
  // Sonamu automatically validates with Zod
}

2. Use Explicit Zod Schema

const CreateUserSchema = z.object({
  email: z.string().email(),
  age: z.number().int().positive()
});

@api({ schema: CreateUserSchema })
async createUser(data: z.infer<typeof CreateUserSchema>) {
  // Type safety guaranteed
}

Intersection Type Error

Symptoms

type WithTimestamps = {
  created_at: Date;
  updated_at: Date;
};

type User = {
  id: number;
  email: string;
} & WithTimestamps; // Intersection type

// Type displayed complexly

Solution

From Sonamu 0.7.29+, intersection/union types are automatically wrapped with parentheses:
// Generated type
type User = {
  id: number;
  email: string;
} & WithTimestamps; // Parentheses added for clarity
Re-run sync after update:
pnpm add sonamu@latest
pnpm sonamu sync

Template Literal Type Error

Symptoms

type EventName = `user:${string}`;

// Error when using template literal type with Zod v4

Cause

Zod v4 changed how template literal types are handled.

Solution

Sonamu automatically handles backslash escaping:
// entity.json
{
  "columns": {
    "event_name": {
      "type": "string",
      "literalType": "user:${string}"  // Automatically handled
    }
  }
}
Generated Zod schema:
z.literal(`user:$\{string}`); // Properly escaped

Circular Reference Type Error

Symptoms

// user.model.ts
export class UserModelClass extends BaseModelClass {
  // ...
}
export type User = { ... posts: Post[] };

// post.model.ts
export class PostModelClass extends BaseModelClass {
  // ...
}
export type Post = { ... author: User };

// Error: Circular dependency detected

Cause

Two entities reference each other, causing circular dependency.

Solutions

1. Use Type-only Import

// user.model.ts
import type { Post } from "../post/post.model";

export type User = {
  id: number;
  email: string;
  posts: Post[];
};
// post.model.ts
import type { User } from "../user/user.model";

export type Post = {
  id: number;
  title: string;
  author: User;
};

2. Create Common Types File

// types/index.ts
export type { User } from "../application/user/user.model";
export type { Post } from "../application/post/post.model";
// Use in other files
import type { User, Post } from "@/types";

File Type Error (@upload)

Symptoms

@upload()
async uploadFile() {
  const { bufferedFiles } = Sonamu.getContext();
  const file = bufferedFiles?.[0];

  const buffer = file.buffer;  // ❌ file may be undefined
}

Cause

The type of bufferedFiles?.[0] is BufferedFile | undefined, so accessing a property without a null check causes a type error.

Solution

@upload()
async uploadFile() {
  const { bufferedFiles } = Sonamu.getContext();
  const file = bufferedFiles?.[0];

  if (!file) {
    throw new BadRequestException("File is required");
  }

  // βœ… Access buffer after null check
  const buffer = file.buffer;

  // Or save file
  const md5 = await file.md5();
  const url = await file.saveToDisk("fs", `uploads/${md5}.${file.extname}`);

  return { url, size: file.size };
}

Type Guard Error

Symptoms

function isUser(value: any): boolean {
  // ❌
  return value && typeof value.id === "number";
}

if (isUser(data)) {
  console.log(data.email); // Error: Property 'email' does not exist
}

Cause

Type guard function doesn’t perform type narrowing.

Solution

Use type predicate:
function isUser(value: any): value is User {
  // βœ…
  return value && typeof value.id === "number" && typeof value.email === "string";
}

if (isUser(data)) {
  console.log(data.email); // βœ… Type safe
}

Generic Type Inference Failure

Symptoms

async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  return response.json(); // Type: any
}

const users = await fetchData("/api/users");
// Type: unknown  ❌

Solution

Specify explicit type:
const users = await fetchData<User[]>("/api/users");
// Type: User[]  βœ…
Or type validation:
async function fetchData<T>(url: string, validator: (data: unknown) => data is T): Promise<T> {
  const response = await fetch(url);
  const data = await response.json();

  if (!validator(data)) {
    throw new Error("Invalid data format");
  }

  return data;
}

// Usage
const users = await fetchData("/api/users", isUserArray);