---
description: Conventions for TypeScript files (.ts, .tsx). Covers clean code principles, TypeScript style, type naming, JSDoc, file organization, imports, and version control.
paths:
  - "src/**/*.ts"
  - "src/**/*.tsx"
---

# TypeScript Conventions

General rules for writing clean, consistent TypeScript code.

---

## 1. Clean Code principles

### Constants over magic numbers
- Replace hard-coded values with named constants
- Use descriptive names that explain purpose
- Keep constants at file top or in dedicated file

### Meaningful names
- Variables, functions, classes reveal their purpose
- Names explain why something exists
- Avoid abbreviations unless universally understood

### Smart comments
- Don't comment what code does — make it self-documenting
- Comment why something is done a certain way
- Document APIs, complex algorithms, non-obvious side effects
- TODO comments are valid only when linked to a tracked ticket — orphan TODOs are not allowed

#### TODO format

Use `// TODO(TICKET-ID): <short description>`:

```typescript
// ✅ valid
// TODO(TICKET-ID): replace polling with websocket once backend supports it

// ❌ invalid — no ticket
// TODO: fix this later

// ❌ invalid — author instead of ticket
// TODO(brian): clean up
```

### Single responsibility
- Each function does exactly one thing
- Functions are small and focused
- If it needs a comment to explain what it does, split it

### DRY (Don't Repeat Yourself)
- Extract repeated code into reusable functions
- Share logic through proper abstraction
- Maintain single sources of truth

### Encapsulation
- Hide implementation details
- Expose clear interfaces
- Move nested conditionals into well-named functions

### Prefer declarative iteration
- Avoid `.reduce()`; prefer readable constructs
- Use `for...of` loops or small helper functions
- Make aggregation logic obvious at a glance

---

## 2. TypeScript style

- Prefer **`type`** over `interface`
- Avoid explicit types when TypeScript can infer
- **No hardcoded strings** in code; use constants or appropriate files
- **No duplicated types** — use module root `types` or `src/types`
- TypeScript strict mode — avoid `any`, prefer proper generics

### Dates
- Outside components: use user locale with date-fns

---

## 3. Type naming and JSDoc

### Naming
- **PascalCase** for all type names.
- Props types use a descriptive suffix: `ProcessFormProps`, `StepActionListProps`.
- Enum-like constants: `UPPER_SNAKE_CASE` for the object, PascalCase for the type.

### JSDoc

All exported types, interfaces, and individual fields should have **JSDoc in English**:

```typescript
/** Props for the ProcessForm component. */
export type ProcessFormProps = {
  /** Parent process ID. */
  processId: string;
  /** Called on successful submission. */
  onSuccess?: () => void;
  /** Disables all form interactions. */
  disabled?: boolean;
};
```

Use JSDoc on exported utility functions and hooks as well:

```typescript
/** Transforms form values into the API request payload. */
export const transformFormToRequest = (values: FormValues): CreateRequest => { ... };
```

---

## 4. File organization

- **Giant single files** — split by domain at ~300 lines
- Keep related code together
- Use consistent file and folder naming

---

## 5. Imports

Import sorting and grouping is enforced by **Biome**. Read `biome.jsonc` and `node_modules/humand-biome-config/biome.jsonc` for the exact rules. Run `npm run biome:fix` to auto-sort.

Key conventions not covered by Biome:

- Use **absolute imports** (`src/...`) for cross-module references
- Use **relative imports** (`./`, `../`) within the same feature/folder
- Prefer **barrel imports** (`from '../services'`) for module-level `services/`, `types/`, etc.

---

## 6. Specific type conventions

### `null` vs `undefined`

- Prefer **`undefined`** for optional or absent values.
- Use **`null`** only when an external API explicitly returns it.

### Avoid native `enum`

Prefer `const` objects with `as const` and a derived type:

```typescript
// NO
enum Status { Active = 'active', Inactive = 'inactive' }

// YES
const Status = {
  Active: 'active',
  Inactive: 'inactive',
} as const;
type Status = (typeof Status)[keyof typeof Status];
```

Native `enum` generates extra runtime code and can cause tree-shaking issues.

### Error typing in `catch`

Caught errors are typed as `unknown`. Narrow before using:

```typescript
try {
  await doSomething();
} catch (error) {
  if (error instanceof Error) {
    console.error(error.message);
  }
}
```

### Non-null assertion (`!`)

Avoid the non-null assertion operator. Prefer types that reflect reality:

```typescript
// NO
const value = maybeNull!.property;

// YES — narrow the type to match actual behavior
function resolve(input: string): string { ... }  // input is never null here
```

If fixing the types correctly would require large, complex changes, **ask the user before proceeding** rather than patching with `!`.

### Type casts (`as`)

Avoid `as` casts. Instead, fix the type at the origin — typically the service or API layer:

```typescript
// NO — casting at the consumer hides the real type mismatch
communityFeatures: response.data.communityFeatures as CommunityFeature[],

// YES — type correctly at the source so the type flows naturally
export const getCommunityFeatures = () =>
  api.get<{ communityFeatures: CommunityFeature[] }>('community-features');
```

If correcting the type at the source would require large, complex changes, **ask the user before proceeding**.

### Prefer declarative iteration — example

```typescript
// NO
const total = items.reduce((acc, item) => acc + item.value, 0);

// YES
let total = 0;
for (const item of items) {
  total += item.value;
}
```
