> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Shared Types

> Type structure of services.generated.ts

Learn about the type structure of `services.generated.ts` generated by Sonamu and how to use it.

## Shared Types Overview

<CardGroup cols={2}>
  <Card title="Single File" icon="file">
    services.generated.ts All types in one place
  </Card>

  <Card title="Auto Generated" icon="wand-magic-sparkles">
    Extracted from backend No manual work needed
  </Card>

  <Card title="Type Reuse" icon="recycle">
    Exported types Use throughout project
  </Card>

  <Card title="Consistency" icon="clone">
    Single source of truth No type conflicts
  </Card>
</CardGroup>

## services.generated.ts Structure

### File Overview

Sonamu generates all types and Services in a single file.

```typescript theme={null}
// services.generated.ts (auto-generated)

// 1. Common Imports
import { useQuery, useMutation, queryOptions } from "@tanstack/react-query";
import qs from "qs";

// 2. Entity Types
export interface User {
  id: number;
  username: string;
  email: string;
  createdAt: Date;
}

// 3. Subset Types
export type UserSubsetKey = "A" | "B" | "C";
export type UserSubsetMapping = {
  A: { id: number; username: string };
  B: { id: number; username: string; email: string };
  C: User;
};

// 4. Service Namespace
export namespace UserService {
  // Service functions
  export async function getUser<T extends UserSubsetKey>(
    subset: T,
    id: number,
  ): Promise<UserSubsetMapping[T]> {
    return fetch({
      method: "GET",
      url: `/api/user/findById?${qs.stringify({ subset, id })}`,
    });
  }

  // TanStack Query Hook
  export const getUserQueryOptions = <T extends UserSubsetKey>(subset: T, id: number) =>
    queryOptions({
      queryKey: ["User", "getUser", subset, id],
      queryFn: () => getUser(subset, id),
    });

  export const useUser = <T extends UserSubsetKey>(
    subset: T,
    id: number,
    options?: { enabled?: boolean },
  ) =>
    useQuery({
      ...getUserQueryOptions(subset, id),
      ...options,
    });
}

// 5. Other Entities follow same structure
export interface Post {
  /* ... */
}
export namespace PostService {
  /* ... */
}
```

**File size**:

* Usually 1,000 \~ 5,000 lines
* Can be 10,000+ lines with many Entities and APIs
* But all auto-generated so no management burden

## Type Categories

### 1. Entity Types

Represents database table structure.

```typescript theme={null}
// User Entity
export interface User {
  id: number;
  username: string;
  email: string;
  role: "admin" | "user";
  bio: string | null;
  createdAt: Date;
  updatedAt: Date;
}

// Post Entity
export interface Post {
  id: number;
  title: string;
  content: string;
  author_id: number;
  published: boolean;
  createdAt: Date;
}
```

**Characteristics**:

* Exactly same as backend Entity
* All field types accurately mapped
* null, undefined, union types all preserved

### 2. Subset Types

Defines subsets of Entity.

```typescript theme={null}
// Subset Key (literal union)
export type UserSubsetKey = "A" | "B" | "C";

// Subset Mapping (type per Key)
export type UserSubsetMapping = {
  A: Pick<User, "id" | "username">;
  B: Pick<User, "id" | "username" | "email">;
  C: User; // All fields
};

// Use as Mapped Type
type SubsetA = UserSubsetMapping["A"];
// { id: number; username: string }
```

**Subset naming convention**:

* **A**: Minimum fields (id + 1\~2 core fields)
* **B**: Medium fields (A + additional info)
* **C**: All fields

### 3. API Parameter Types

Defines API function parameters.

```typescript theme={null}
export namespace UserService {
  // Explicit parameter types
  export async function updateProfile(params: {
    username?: string;
    bio?: string;
    avatar?: string;
  }): Promise<{ user: User }> {
    return fetch({
      method: "PUT",
      url: "/api/user/updateProfile",
      data: params,
    });
  }

  // Complex search parameters
  export async function search(query: {
    keyword: string;
    role?: "admin" | "user";
    isActive?: boolean;
    page?: number;
    pageSize?: number;
  }): Promise<{ users: User[]; total: number }> {
    return fetch({
      method: "GET",
      url: `/api/user/search?${qs.stringify(query)}`,
    });
  }
}
```

### 4. API Response Types

Defines API function return types.

```typescript theme={null}
export namespace UserService {
  // Simple response
  export async function getUser(id: number): Promise<{ user: User }> {
    // ...
  }

  // Complex response
  export async function getDashboard(): Promise<{
    user: User;
    stats: {
      postCount: number;
      followerCount: number;
      viewCount: number;
    };
    recentPosts: Post[];
    recentComments: Comment[];
  }> {
    // ...
  }
}
```

### 5. TanStack Query Related Types

Types used in React Hooks.

```typescript theme={null}
export namespace UserService {
  // Query Options type (reusable)
  export const getUserQueryOptions = (id: number) =>
    queryOptions({
      queryKey: ["User", "getUser", id],
      queryFn: () => getUser(id),
    });

  // Hook type (auto-inferred)
  export const useUser = (id: number, options?: { enabled?: boolean }) =>
    useQuery({
      ...getUserQueryOptions(id),
      ...options,
    });
}

// Types auto-inferred on usage
const { data, isLoading } = UserService.useUser(123);
// data type: { user: User } | undefined
```

## Type Reuse

### Using Type Helpers

Reuse existing types with TypeScript's utility types.

```typescript theme={null}
import type { UserService, User } from "@/services/services.generated";

// 1. Extract function return type
type UserProfile = Awaited<ReturnType<typeof UserService.getProfile>>;
// { user: User; stats: { postCount: number; ... } }

// 2. Extract parameter type
type UpdateParams = Parameters<typeof UserService.updateProfile>[0];
// { username?: string; bio?: string; avatar?: string }

// 3. Entity partial type
type UserBasic = Pick<User, "id" | "username" | "email">;

// 4. All fields optional
type PartialUser = Partial<User>;

// 5. Exclude specific fields
type UserWithoutDates = Omit<User, "createdAt" | "updatedAt">;
```

### Component Props

Use generated types as Props.

```typescript theme={null}
import type { User, Post } from "@/services/services.generated";

// Use Entity type directly
interface UserCardProps {
  user: User;
}

function UserCard({ user }: UserCardProps) {
  return (
    <div>
      <h3>{user.username}</h3>
      <p>{user.email}</p>
    </div>
  );
}

// Combine multiple Entities
interface DashboardProps {
  user: User;
  posts: Post[];
}

function Dashboard({ user, posts }: DashboardProps) {
  return (
    <div>
      <h1>Welcome, {user.username}</h1>
      {posts.map((post) => (
        <PostCard key={post.id} post={post} />
      ))}
    </div>
  );
}
```

### Form Data Types

Use API parameter types as form data.

```typescript theme={null}
import { useState } from "react";
import type { UserService } from "@/services/services.generated";

// Extract parameter type
type UpdateProfileParams = Parameters<typeof UserService.updateProfile>[0];

function EditProfileForm() {
  // Type-safe state
  const [formData, setFormData] = useState<UpdateProfileParams>({
    username: "",
    bio: "",
    avatar: "",
  });

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    await UserService.updateProfile(formData); // ✅ Types match
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={formData.username}
        onChange={(e) => setFormData({ ...formData, username: e.target.value })}
      />
      {/* ... */}
    </form>
  );
}
```

### State Management

Reuse types in global state as well.

```typescript theme={null}
import { create } from "zustand";
import type { User } from "@/services/services.generated";

// Zustand Store
interface AuthStore {
  user: User | null;
  login: (user: User) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthStore>((set) => ({
  user: null,
  login: (user) => set({ user }),
  logout: () => set({ user: null }),
}));
```

## Namespace Usage

### Service Grouping

Services are grouped by Entity using Namespace.

```typescript theme={null}
// services.generated.ts
export namespace UserService {
  export async function getUser() {
    /* ... */
  }
  export async function updateUser() {
    /* ... */
  }
  export async function deleteUser() {
    /* ... */
  }
  export const useUser = () => {
    /* ... */
  };
}

export namespace PostService {
  export async function getPost() {
    /* ... */
  }
  export async function createPost() {
    /* ... */
  }
  export const usePost = () => {
    /* ... */
  };
}
```

**Usage**:

```typescript theme={null}
// ✅ Clearly distinguished by Namespace
import { UserService, PostService } from "@/services/services.generated";

await UserService.getUser("A", 123);
await PostService.getPost("A", 456);
```

**Benefits**:

* Prevents name collisions (getUser vs getPost)
* Related functions logically grouped
* Imports become concise
* IDE auto-completion becomes more accurate

### Type Import

Use `type` keyword when importing types only.

```typescript theme={null}
// ✅ Import types only (reduces bundle size)
import type { User, Post } from "@/services/services.generated";

// ❌ Full import (includes unnecessary code)
import { User, Post } from "@/services/services.generated";
```

**Tree-shaking**:

* `import type` is removed after compilation
* Bundle size optimization
* Build speed improvement

## File Size Management

### Large-scale Projects

Files can become very large with many Entities and APIs.

```typescript theme={null}
// services.generated.ts (example)
// 100 Entities × average 50 lines = 5,000 lines
// + Service functions = 10,000+ lines
```

**But it's okay**:

1. ✅ **Auto-generated**: No manual management needed
2. ✅ **Tree-shaking**: Unused code excluded from bundle
3. ✅ **IDE performance**: Modern IDEs handle large files fine
4. ✅ **Type checking**: TypeScript compiler processes efficiently

### Code Splitting

You can split code with dynamic import if needed.

```typescript theme={null}
// ✅ Load only when needed
async function loadUserService() {
  const { UserService } = await import("@/services/services.generated");
  return UserService;
}

// Usage
const UserService = await loadUserService();
await UserService.getUser("A", 123);
```

## Version Control

### Include in Git or Not

**Include (recommended)**:

```gitignore theme={null}
# Include services.generated.ts in Git
# (comment out or remove)
# services.generated.ts
```

**Benefits**:

* Ready to use immediately after pull
* Can develop frontend without backend
* Can track type change history

**Don't include**:

```gitignore theme={null}
# Exclude services.generated.ts from Git
services.generated.ts
```

**Benefits**:

* Less conflicts
* Each person generates latest version
* Git history stays clean

**Recommendation**: **Including** is generally more convenient.

### Resolving Conflicts

When merge conflict occurs:

```bash theme={null}
# 1. Pull latest code
git pull origin main

# 2. Regenerate Service
pnpm generate

# 3. Resolve conflict with regenerated file
git add services.generated.ts
git commit -m "resolve: regenerate services"
```

## Debugging

### Check Generated Types

You can check types directly in IDE.

```typescript theme={null}
import { UserService } from "@/services/services.generated";

// Hover over function
UserService.getUser;
// ↓ IDE shows type
// (alias) getUser<T extends UserSubsetKey>(
//   subset: T,
//   id: number
// ): Promise<UserSubsetMapping[T]>
```

**Shortcut**:

* VSCode: `Cmd + Click` (Mac) / `Ctrl + Click` (Windows)
* Jump directly to type definition

### Debugging Type Errors

When type error occurs:

1. **Check error message**

```
Property 'username' does not exist on type 'User'.
```

2. **Check type definition**

```typescript theme={null}
// Check User type in services.generated.ts
export interface User {
  id: number;
  displayName: string; // Not username!
  email: string;
}
```

3. **Check backend**

```typescript theme={null}
// Check actual definition in backend
@api({ httpMethod: "GET" })
async getUser(): Promise<{ user: { displayName: string } }> {
  // displayName, not username!
}
```

4. **Fix code**

```typescript theme={null}
// username → displayName
console.log(user.displayName);
```

## Cautions

<Warning>
  **Cautions when using shared types**: 1. **Don't manually modify services.generated.ts** 2. Use
  `import type` for bundle size optimization 3. **pnpm generate required** on API changes 4. Access
  Services through Namespace 5. **Use Type Helpers** when reusing types
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="API Type Inference" icon="wand-magic-sparkles" href="/en/frontend-integration/type-safety/api-type-inference">
    Understanding type inference
  </Card>

  <Card title="Compile-time Errors" icon="triangle-exclamation" href="/en/frontend-integration/type-safety/compile-time-errors">
    Error detection methods
  </Card>

  <Card title="Using Services" icon="code" href="/en/frontend-integration/generated-services/using-services">
    Service usage
  </Card>

  <Card title="Custom Components" icon="sliders" href="/en/frontend-integration/react-components/custom-components">
    Writing components
  </Card>
</CardGroup>
