---
description: Guidelines for TypeScript usage, including type safety rules and Convex query typing
globs: **/*.ts,**/*.svelte
alwaysApply: false
---

# TypeScript Guidelines

## Type Safety Rules

### Avoid `any` Type

- **NEVER** use the `any` type in production code
- The only exception is in test files (files matching `*.test.ts`, `*.test.tsx`, `*.spec.ts`, `*.spec.tsx`)
- Instead of `any`, use:
  - Proper type definitions
  - `unknown` for truly unknown types (with type guards)
  - Generic types (`<T>`) when appropriate
  - Union types when multiple types are possible
  - `Record<string, unknown>` for objects with unknown structure

### Examples

**❌ Bad:**

```typescript
function processData(data: any) {
	return data.value;
}
```

**✅ Good:**

```typescript
function processData(data: { value: string }) {
	return data.value;
}

// Or with generics
function processData<T extends { value: unknown }>(data: T) {
	return data.value;
}

// Or with unknown and type guards
function processData(data: unknown) {
	if (typeof data === 'object' && data !== null && 'value' in data) {
		return (data as { value: string }).value;
	}
	throw new Error('Invalid data');
}
```

**✅ Exception (tests only):**

```typescript
// test.ts or *.spec.ts
it('should handle any input', () => {
	const input: any = getMockData();
	expect(process(input)).toBeDefined();
});
```

## Convex Query Typing

### Frontend Query Usage

- **DO NOT** create manual type definitions for Convex query results in the frontend
- Convex queries already return properly typed results based on their `returns` validator
- The TypeScript types are automatically inferred from the query's return validator
- Simply use the query result directly - TypeScript will infer the correct type

### Examples

**❌ Bad:**

```typescript
// Don't manually type the result
type UserListResult = Array<{
	_id: Id<'users'>;
	name: string;
}>;

const users: UserListResult = useQuery(api.users.list);
```

**✅ Good:**

```typescript
// Let TypeScript infer the type from the query
const users = useQuery(api.users.list);
// TypeScript automatically knows the type based on the query's returns validator

// You can still use it with type inference
if (users !== undefined) {
	users.forEach((user) => {
		// TypeScript knows user._id is Id<"users"> and user.name is string
		console.log(user.name);
	});
}
```

**✅ Good (with explicit type if needed for clarity):**

```typescript
// Only if you need to export or explicitly annotate for documentation
import type { FunctionReturnType } from 'convex/server';
import type { api } from './convex/_generated/api';

type UserListResult = FunctionReturnType<typeof api.users.list>;
const users = useQuery(api.users.list);
```

### Best Practices

- Trust Convex's type inference - it's based on your schema and validators
- If you need type annotations, use `FunctionReturnType` from Convex's type utilities
- Only create manual types if you're doing complex transformations that need intermediate types
