# Step 9 — The Shared Contract: `shared/types.ts`

Step 7's `Tag`, `CreateTag`, and `UpdateTag` Rust structs need a TypeScript equivalent so the frontend can work with the same shapes. That equivalent lives in `shared/types.ts` (imported via the `shared` alias from Step 3) — a single file the whole frontend imports its API types from.

## The three types, side by side with Rust

```ts
export type Tag = {
  id: string,
  tag_name: string,
  content: string,
  color: string | null,
  team_id: string | null,
  created_at: string,
  updated_at: string,
};

export type CreateTag = {
  tag_name: string,
  content: string,
  color?: string | null,
  team_id?: string | null,
};

export type UpdateTag = {
  tag_name: string | null,
  content: string | null,
  color?: string | null,
  team_id?: string | null,
};
```

Compare this to Step 7's Rust:
- `Uuid` → `string`, `DateTime<Utc>` → `string` (JSON has no native UUID or date type — both cross the wire as strings, per CLAUDE.md core rule #7).
- Rust's `Option<String>` → TypeScript's `string | null` — the same "this might be absent" idea, expressed with each language's native tool for it.
- `CreateTag`/`UpdateTag` keep the exact same "narrower struct for writes" shape from Step 7 — the frontend's types are a direct mirror, not a reinterpretation.

## Why this file is hand-maintained, not generated

You might expect a tool to generate `types.ts` automatically from the Rust structs — and iKanban used to run one (`ts-rs`). That generator is **retired**; `shared/types.ts` is now edited by hand whenever a Rust struct changes shape. In practice this means: if you change a field in `crates/remote/src/db/tags.rs`, you must **also** update `shared/types.ts` yourself — nothing enforces the two stay in sync automatically. This is exactly the kind of contract Step 7's preflight-style thinking exists to protect: a silently-drifted shared type doesn't fail at compile time, it fails as a runtime shape mismatch the first time real data flows through it.

## Where else this type is used

`Tag` doesn't just describe one row — CLAUDE.md's "borrow before you build" principle shows up here too. Rather than each frontend file inventing its own tag shape, everything downstream imports the same `Tag` from `shared/types`:

```ts
import type { CreateTag, Tag, UpdateTag } from 'shared/types';
```

You'll see this exact import in Step 10's API client, Step 11's React hook, and Step 12's dialog component — one type, one source of truth, four consumers.

## Checkpoint

Search the frontend for `TaskTagWithDetails` in `shared/types.ts` — it's a *fourth*, denormalized shape (a tag joined with its assignment to a specific task) that exists purely because a task's tag list needs more context than a bare `Tag` provides. Notice it's a distinct type rather than `Tag` with extra optional fields bolted on — the same "don't overload one struct for every use case" instinct from Step 7's `CreateTag`/`UpdateTag` split.

Continue to **Step 10 — The API Client: `tagsApi` in `lib/api.ts`**, where these types get used to call the routes from Step 8.
