# Step 2 — TypeScript Essentials for React

Before touching JSX, let's cover the handful of TypeScript patterns that show up in nearly every iKanban component. We'll read them straight out of `src/components/AgentModeBadge.tsx` — a small, real component that renders a colored badge describing whether an AI agent runs via CLI, API, both, or neither.

## 1. A union of string literals

```ts
export type AvailabilityMode = 'cli_only' | 'api_only' | 'both' | 'none';
```

This is **not** an enum — it's a type that can only ever be one of these four exact strings. Pass anything else and TypeScript rejects it at compile time. This pattern shows up everywhere in iKanban: task `status`, issue `priority`, agent lifecycle state — any field with a small, fixed set of valid values is modeled this way instead of a free-form `string`.

## 2. Props as an interface

```ts
interface AgentModeBadgeProps {
  mode: AvailabilityMode;
  className?: string;
  showTooltip?: boolean;
}

export function AgentModeBadge({
  mode,
  className,
  showTooltip = true,
}: AgentModeBadgeProps) {
  // ...
}
```

Every component's inputs are an `interface` (or `type`), destructured directly in the function signature. `className?` and `showTooltip?` are **optional** — the `?` means callers can omit them. `showTooltip = true` is a default value applied during destructuring, a plain JavaScript feature that pairs naturally with optional TS props.

## 3. A lookup object keyed by the union, typed as a const

```ts
const MODE_CONFIG = {
  cli_only: { icon: Monitor, labelKey: 'settings.agents.mode.cliOnly', /* ... */ },
  api_only: { icon: Cloud, labelKey: 'settings.agents.mode.apiOnly', /* ... */ },
  both: { icon: RefreshCw, labelKey: 'settings.agents.mode.both', /* ... */ },
  none: { icon: XCircle, labelKey: 'settings.agents.mode.none', /* ... */ },
} as const;
```

Because the object's keys are exactly the four `AvailabilityMode` values, `MODE_CONFIG[mode]` is guaranteed to exist for any valid `mode` — no `undefined` check needed. This is the standard way iKanban maps a union type to per-variant configuration (icon, label, styling) without a chain of `if`/`else`.

## 4. `as const` narrows literal types

Without `as const`, TypeScript would widen `variant: 'secondary'` to the general type `string`. With it, TypeScript keeps the exact literal `'secondary'`, which matters because the `<Badge variant={...}>` prop only accepts a specific set of literal strings, not any string.

## Putting it together

```ts
const config = MODE_CONFIG[mode];
const Icon = config.icon;

return (
  <Badge variant={config.variant}>
    <Icon className="h-3 w-3" />
    {t(config.labelKey)}
  </Badge>
);
```

`Icon` here is a **component reference stored in a variable**, then rendered with `<Icon />`. This is how iKanban swaps icons dynamically based on data — you'll see the same trick in the sidebar icon map in Step 4.

## Checkpoint

Open `vibe-frontend/src/components/AgentModeBadge.tsx` yourself and find:
1. Where the optional `showTooltip` prop changes what gets rendered (hint: an early `return`).
2. How `mode === 'both'` conditionally appends extra Tailwind classes to the badge.

Continue to **Step 3 — Vite, TypeScript Config & Project Structure**, where we cover how these `.tsx` files get built and how `@/` imports resolve.
