diff --git a/docs/guidance/playwright.md b/docs/guidance/playwright.md index 75eef9aa9..29fc4a0a4 100644 --- a/docs/guidance/playwright.md +++ b/docs/guidance/playwright.md @@ -29,6 +29,52 @@ This is the **only context** where non-null assertions are acceptable. **TODO:** Consolidate these references into a central utility (e.g., `getApp()`) that performs proper runtime type checking, removing the need for scattered `!` assertions. +## Type Assertions in E2E Tests + +E2E tests may use **specific** type assertions when needed, but **never** `as any`. + +### Acceptable Patterns + +```typescript +// ✅ Non-null assertions for window globals +window.app!.extensionManager + +// ✅ Specific type assertions with documentation +// Extensions can register arbitrary setting IDs +id: 'TestSetting' as TestSettingId + +// ✅ Test-local type helpers +type TestSettingId = keyof Settings +``` + +### Forbidden Patterns + +```typescript +// ❌ Never use `as any` +settings: testData as any + +// ❌ Never modify production types to satisfy test errors +// Don't add test settings to src/schemas/apiSchema.ts + +// ❌ Don't chain through unknown to bypass types +data as unknown as SomeType // Use sparingly, document why +``` + +### Accessing Internal State + +When tests need internal store properties (e.g., `.workflow`, `.focusMode`): + +```typescript +// ✅ Access stores directly in page.evaluate +await page.evaluate(() => { + const store = useWorkflowStore() + return store.activeWorkflow +}) + +// ❌ Don't change public API types to expose internals +// Keep app.extensionManager typed as ExtensionManager, not WorkspaceStore +``` + ## Test Tags Tags are respected by config: diff --git a/docs/guidance/typescript.md b/docs/guidance/typescript.md index 9a6dd102b..17b2f3989 100644 --- a/docs/guidance/typescript.md +++ b/docs/guidance/typescript.md @@ -14,6 +14,28 @@ globs: - Type assertions are a last resort; they lead to brittle code - Avoid `@ts-expect-error` - fix the underlying issue instead +### Type Assertion Hierarchy + +When you must handle uncertain types, prefer these approaches in order: + +1. ✅ **No assertion** — Properly typed from the start +2. ✅ **Type narrowing** — `if ('prop' in obj)` or type guards +3. ⚠️ **Specific assertion** — `as SpecificType` when you truly know the type +4. ⚠️ **`unknown` with narrowing** — For genuinely unknown data +5. ❌ **`as any`** — FORBIDDEN + +### Zod Schema Rules + +- Never use `z.any()` — it disables validation and propagates `any` into types +- Use `z.unknown()` if the type is genuinely unknown, then narrow it +- Never add test-only settings/types to production schemas + +### Public API Contracts + +- Keep public API types stable (e.g., `ExtensionManager` interface) +- Don't expose internal implementation types (e.g., Pinia store internals) +- Reactive refs (`ComputedRef`) should be unwrapped before exposing + ## Utility Libraries - Use `es-toolkit` for utility functions (not lodash)