<!-- skill: TypeScript Codebase | version: 1.0.0 | description: Strict TypeScript setup, types, tsconfig and JS migration guide | category: Web -->

## Instructions
- Enable `strict: true` (plus `noUncheckedIndexedAccess`, `noImplicitOverride`) from day one; never ship with `strict: false` to silence errors.
- Prefer `interface` for object shapes, `type` for unions/intersections/mapped types; export shared types from `src/types.ts`.
- Type functions explicitly (params + return); avoid `any` — use `unknown` + narrowing, or `never` for exhaustive checks.
- Model absence with `| null | undefined` + optional chaining; validate external/JSON data at runtime with zod/valibot, not casts.
- Use generics for reusable containers (`ApiResponse<T>`, `Paginated<T>`); constrain with `extends` instead of casting inside.
- Separate `.ts` (logic) from `.tsx` (JSX); keep components typed via props interfaces, event types (`React.ChangeEvent<HTMLInputElement>`).
- Configure `tsconfig.json` deliberately: `target ES2022`, `module ESNext`, `moduleResolution Bundler`, `isolatedModules`, `skipLibCheck`, path alias `@/*`.
- Migrate JS→TS incrementally: rename to `.ts`, set `allowJs: true` + `checkJs: false` initially, tighten file-by-file, then flip strict on.
- Handle DOM/Node libs via `lib`/`types` fields; install `@types/*` for untyped deps; never hand-write ambient decls when DefinitelyTyped exists.
- Lint + format: typescript-eslint (`recommended-type-checked`), Prettier; run `tsc --noEmit` in CI and pre-commit via `lint-staged`.
- Build with Vite (`vite build` + `tsc --noEmit` separately); emit declarations for libraries (`declaration: true`, `tsup` or `tsc -p tsconfig.build.json`).
- Document complex types with TSDoc comments and a `docs/types.md` map so agents know which type lives where.

## Examples
- Typed fetch client: generic `api<T>()` returning `ApiResponse<T>` with discriminated `ok/error` union, zod-validated at runtime.
- Migration slice: `utils/format.js` → `format.ts` with overloads, `tsc` errors fixed by narrowing `unknown` instead of `as any`.
- Vite React-TS app: aliased imports (`@/components/Button`), strict tsconfig, props interfaces, `tsc --noEmit` gate in `npm run build`.

## Workflows
1. Scaffold: `npm create vite@latest app -- --template react-ts`, install deps, drop in strict tsconfig below.
2. Define domain types first (`User`, `Order`, `ApiResponse<T>`) before any UI or API code.
3. Implement with TDD-ish loop: write types → code → `tsc --noEmit` → fix by narrowing, not casting.
4. Add zod schemas at every network/storage boundary; parse before assigning to typed state.
5. Gate releases: `tsc --noEmit` + eslint + `vite build` all green; publish libs with `.d.ts` + sourcemaps.

## Templates
```json
{
  "compilerOptions": {
    "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler",
    "strict": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true,
    "skipLibCheck": true, "isolatedModules": true, "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true, "resolveJsonModule": true,
    "declaration": true, "sourceMap": true, "noEmit": true,
    "baseUrl": ".", "paths": { "@/*": ["src/*"] },
    "lib": ["ES2022", "DOM", "DOM.Iterable"], "types": ["vite/client"]
  },
  "include": ["src"]
}
```
```ts
// src/api.ts — typed client with narrowing
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), email: z.string().email() });
export type User = z.infer<typeof UserSchema>;
export async function api<T>(path: string, schema: z.ZodType<T>, init?: RequestInit): Promise<T> {
  const res = await fetch(path, { headers: { 'Content-Type': 'application/json' }, ...init });
  const json: unknown = await res.json().catch(() => null);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return schema.parse(json); // runtime + compile-time safety
}
export function assertNever(x: never): never { throw new Error(`Unexpected: ${JSON.stringify(x)}`); }
```

## Tools
- SARAH `shell_run`: `npx tsc --noEmit` to typecheck; `npm run build` (tsc + vite) before every deploy.
- SARAH `file_write`: place `tsconfig.json` at root, sources under `src/`, shared types in `src/types.ts`.
- SARAH preview: `npm run dev` serves Vite-TS; confirm HMR works and no overlay type errors remain.

## Advanced
- Narrowing toolkit: `in` checks, discriminated unions (`{ kind: 'ok' } | { kind: 'err' }`), assertion functions, exhaustive `switch` ending in `assertNever`.
- Generics patterns: `function first<T>(xs: readonly T[]): T | undefined`, `Record<string, T>` maps, `Awaited<ReturnType<typeof fn>>` inference.
- Troubleshooting: `Object is possibly undefined` → enable indexing guard or add explicit check; `any` leaking from JSON → type as `unknown` then zod-parse; slow `tsc` → `skipLibCheck`, project references, `tsc --extendedDiagnostics`.
- Vite gotchas: `import.meta.env` needs `/// <reference types="vite/client" />` or `types` entry; path alias needs matching `vite.config.ts` `resolve.alias`.
- Pro tip: `satisfies` operator (`const cfg = {...} satisfies Config`) keeps literals narrow while checking shape — prefer over `as Config`.

## Documentation
- TypeScript handbook: https://www.typescriptlang.org/docs/handbook/intro.html
- Vite guide: https://vite.dev/guide
- zod: https://zod.dev
