# Code

> Use when writing TypeScript/React code - covers type safety, component patterns, and file organization

- Skill: `trycompai/code` (Agent Skill)
- Install (CLI): `npx skillmds@latest add trycompai/code`
- Raw SKILL.md: https://api.skillmd.com/api/skills/trycompai/code/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: trycompai (https://skillmd.com/u/trycompai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/trycompai/code

---


Source Cursor rule: `.cursor/rules/code.mdc`.
Original Cursor alwaysApply: `false`.

# Code Standards

## TypeScript

### No `any`, No Unsafe Casts

```tsx
// ✅ Validate with zod
const TaskSchema = z.object({ id: z.string(), title: z.string() });
const task = TaskSchema.parse(response.data);

// ✅ Use unknown and narrow
const parseResponse = (data: unknown): Task => {
  if (!isTask(data)) throw new Error('Invalid');
  return data;
};

// ❌ Never
const data: any = fetchData();
const task = response as Task;
const name = user!.name;
// @ts-ignore
```

### Generics Over Any

```tsx
// ✅ Generic
const first = <T>(items: T[]): T | undefined => items[0];

// ❌ Any
const first = (items: any[]): any => items[0];
```

## React Patterns

### Named Exports, PascalCase

```tsx
// ✅ Named export, PascalCase file
// TaskCard.tsx
export function TaskCard({ task }: TaskCardProps) { ... }

// ❌ Default export, lowercase
export default function taskCard() { ... }
```

### Derive State, Avoid useEffect

```tsx
// ✅ Derived
const completedCount = tasks.filter(t => t.completed).length;

// ❌ Synced state
const [count, setCount] = useState(0);
useEffect(() => {
  setCount(tasks.filter(t => t.completed).length);
}, [tasks]);
```

### When useEffect IS Appropriate

```tsx
// External subscriptions
useEffect(() => {
  const sub = eventSource.subscribe(handler);
  return () => sub.unsubscribe();
}, []);

// DOM measurements
useEffect(() => {
  setHeight(ref.current?.getBoundingClientRect().height);
}, []);
```

### Toasts with Sonner

```tsx
import { toast } from 'sonner';

toast.success('Task created');
toast.error('Failed to save');
toast.promise(saveTask(), {
  loading: 'Saving...',
  success: 'Saved!',
  error: 'Failed',
});
```

## File Structure

### Colocate at Route Level

```
app/(app)/[orgId]/tasks/
├── page.tsx              # Server component
├── components/
│   └── TaskList.tsx      # Client component
├── hooks/
│   └── useTasks.ts       # SWR hook
└── data/
    └── queries.ts        # Server queries
```

### Share Only When Reused 3+ Times

```
src/components/shared/    # Cross-page components
src/hooks/                # Shared hooks (useApiSWR, useDebounce)
```

## Code Quality

### File Size Limit: 300 Lines

Split large files into focused components.

### Named Parameters for 2+ Args

```tsx
// ✅ Named
const createTask = ({ title, assigneeId }: CreateTaskParams) => { ... };
createTask({ title: 'Review PR', assigneeId: user.id });

// ❌ Positional
const createTask = (title: string, assigneeId: string) => { ... };
createTask('Review PR', user.id); // What's the 2nd param?
```

### Early Returns

```tsx
// ✅ Early return
function processTask(task: Task | null) {
  if (!task) return null;
  if (task.deleted) return null;
  return <TaskCard task={task} />;
}

// ❌ Nested
function processTask(task) {
  if (task) {
    if (!task.deleted) {
      return <TaskCard task={task} />;
    }
  }
  return null;
}
```

### Event Handler Naming

```tsx
// ✅ Prefix with "handle"
const handleClick = () => { ... };
const handleSubmit = (e: FormEvent) => { ... };
const handleTaskCreate = (task: Task) => { ... };
```

## Accessibility

```tsx
// Interactive elements need keyboard support
<div
  role="button"
  tabIndex={0}
  onClick={handleClick}
  onKeyDown={(e) => e.key === 'Enter' && handleClick()}
  aria-label="Delete task"
>
  <TrashIcon />
</div>

// Form inputs need labels
<label htmlFor="task-name">Task Name</label>
<input id="task-name" type="text" />
```

