090
LVL 04 — SENIOR-IN-TRAININGSESSION 090DAY 90

TYPESCRIPT FULL MIGRATION

🎫 PIXELCRAFT-076
🐛Fix | 🔴 Expert | Priority: 🟠 High

Five files are TypeScript, the rest are JavaScript. Every JS→TS boundary is an any-shaped hole in the type safety net. Complete the migration. Every React component, every API route, every utility — fully typed. Enable strict mode. Zero any.
CONCEPTS.UNLOCKED
🎣
Generics in Hooks
useState<User | null>(null). Tell React exactly what type your state holds. useReducer with typed actions. Custom hooks return typed values — consumers get full autocomplete.
🔲
Typed Context
createContext<EditorState>. Every component that uses the context knows exactly what properties are available. No more guessing. No more undefined access.
📡
Typed API Responses
Define response types for every endpoint. fetch<ApiResponse<Image[]>>. The frontend knows the exact shape of every API response — autocomplete from the network layer to the UI.
🔒
Strict Mode
"strict": true enables all safety checks. strictNullChecks: null must be handled explicitly. noImplicitAny: no untyped values allowed. The highest level of TypeScript safety.
🛠️
Utility Types
Partial<User>, Pick<Image, 'id' | 'title'>, Omit<User, 'password'>. Transform existing types without redefining. Create update shapes from full types. Remove sensitive fields from responses.
🔀
Discriminated Unions
type Result = { status: 'ok'; data: T } | { status: 'error'; message: string }. The status field narrows the type. If status is 'ok', TS knows data exists. If 'error', message exists. Exhaustive handling.
HANDS-ON.TASKS
01
Typed React Hooks
// Typed useState const [user, setUser] = useState<User | null>(null); // Typed useReducer type EditorAction = | { type: 'SET_IMAGE'; payload: Image } | { type: 'APPLY_FILTER'; payload: Filter } | { type: 'UNDO' } | { type: 'REDO' } | { type: 'SET_ZOOM'; payload: number }; function editorReducer( state: EditorState, action: EditorAction ): EditorState { switch (action.type) { case 'SET_IMAGE': return { ...state, currentImage: action.payload }; case 'APPLY_FILTER': return { ...state, activeFilter: action.payload, isDirty: true }; case 'UNDO': return { ...state, historyIndex: state.historyIndex - 1 }; // TS enforces: every case handled } } // Custom hook with typed return function useEditor() { const [state, dispatch] = useReducer(editorReducer, initialState); const applyFilter = (filter: Filter): void => { dispatch({ type: 'APPLY_FILTER', payload: filter }); }; return { state, applyFilter } as const; // as const = readonly tuple }
02
Typed Context
// contexts/AuthContext.tsx interface AuthContextType { user: User | null; token: string | null; login: (email: string, password: string) => Promise<void>; logout: () => void; isAuthenticated: boolean; } const AuthContext = createContext<AuthContextType | null>( null); // Typed hook with null guard function useAuth(): AuthContextType { const context = useContext(AuthContext); if (!context) { throw new Error( 'useAuth must be used within ' + 'AuthProvider'); } return context; } // Now in any component: const { user, login, logout } = useAuth(); // user is User | null // login is fully typed // TS catches: user.name // (might be null — handle it!)
03
Typed API Layer
// api/client.ts interface ApiResponse<T> { data: T; message?: string; } interface ApiError { error: string; details?: Array<{ field: string; message: string; }>; requestId: string; } type Result<T> = | { ok: true; data: T } | { ok: false; error: ApiError }; async function apiGet<T>( path: string ): Promise<Result<T>> { try { const res = await fetch( `/api/v1${path}`, { headers: { Authorization: `Bearer ${getToken()}`, }, }); if (!res.ok) { const error: ApiError = await res.json(); return { ok: false, error }; } const data: T = await res.json(); return { ok: true, data }; } catch (e) { return { ok: false, error: { error: 'Network error', requestId: 'local', }}; } } // Usage — TS knows the shape: const result = await apiGet<Image[]>('/images'); if (result.ok) { // result.data is Image[] result.data.map(img => img.title); } else { // result.error is ApiError console.error(result.error.error); }
04
Typed Express Routes
// server/routes/images.ts import { Request, Response } from 'express'; interface AuthRequest extends Request { userId: string; } interface PaginationQuery { page: number; limit: number; sort: 'createdAt' | 'name' | 'size'; } type CreateImageBody = Pick<Image, 'title'> & Partial<Pick<Image, 'description' | 'tags'>>; router.get('/images', authenticate, async ( req: AuthRequest, res: Response ) => { const { page, limit } = req.query as PaginationQuery; const images = await Image.find({ owner: req.userId }) .skip((page - 1) * limit) .limit(limit); const response: ApiResponse<Image[]> = { data: images }; res.json(response); });
05
Enable Strict Mode
// tsconfig.json — the final step { "compilerOptions": { "strict": true, // Enables ALL of these: // strictNullChecks // noImplicitAny // strictFunctionTypes // strictBindCallApply // strictPropertyInitialization // noImplicitThis // alwaysStrict "allowJs": false, // ↑ No more JS files allowed "noUncheckedIndexedAccess": true, // ↑ array[0] might be undefined } } // Run: npx tsc --noEmit // Fix every error. // When it compiles clean: you're done.
"strict": true is the graduation test. When every file compiles under strict mode with zero errors, your codebase has full type safety. Every null is handled. Every any is eliminated.
06
Close the Ticket
git switch -c feature/PIXELCRAFT-076-typescript-migration git add src/ server/ types/ tsconfig.json git commit -m "Complete TypeScript migration + strict mode (PIXELCRAFT-076)" git push origin feature/PIXELCRAFT-076-typescript-migration # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

TypeScript's type system is Turing-complete.

The type system is so powerful it can compute at compile time. You can write type-level programs that validate data shapes, parse strings, and enforce constraints — all before a single line of JavaScript runs.

// TypeScript type-level programming:

Utility types (built-in)
  Partial<T>  — all fields optional
  Required<T> — all fields required
  Pick<T, K>  — subset of fields
  Omit<T, K>  — exclude fields
  Record<K, V> — key-value map

Conditional types
  T extends U ? X : Y
  (if-else at the type level)

Template literal types
  type Route = `/api/${string}`
  (regex-like patterns for types)

Mapped types
  { [K in keyof T]: T[K] | null }
  (transform every field)

// The type system prevents bugs
// that tests can't even express.
"Type Safety Lab"
[A]Create a type-safe event emitter: EventEmitter<Events> where Events is a map of event names to payload types. emitter.emit('upload', imageData) — TS enforces the payload matches the event name.
[B]Use discriminated unions for the API layer: type Result<T> = Success<T> | Failure. Every API call forces the consumer to handle both cases. No more unchecked .data access on error responses.
[C]Research: TypeScript's structural typing means { name: string; age: number } is compatible with { name: string } — extra properties are allowed. This is by design but can cause bugs. Explore branded types and the "excess property check" for stricter safety.
REF.MATERIAL
ARTICLE
Microsoft
Generics, keyof, typeof, indexed access, conditional types, mapped types, and template literal types. The advanced type system toolkit.
TYPESCRIPTADVANCEDESSENTIAL
VIDEO
Matt Pocock
Deep dive into generics, inference, conditional types, and real-world patterns. The best advanced TS content on YouTube.
TYPESCRIPTADVANCED
ARTICLE
Microsoft
Official reference for Partial, Required, Readonly, Pick, Omit, Record, Extract, Exclude, ReturnType, and more. The type-level standard library.
UTILITY TYPESOFFICIAL
ARTICLE
Community
Practical patterns: typing props, hooks, context, events, refs, and forms in React with TypeScript. Copy-paste solutions for common patterns.
REACTTYPESCRIPTESSENTIAL
VIDEO
Jack Herrington
Generics from basics to advanced: constraints, defaults, inference, and building type-safe utility functions. Practical and project-focused.
GENERICSTUTORIAL
// LEAVE EXCITED BECAUSE
npx tsc --noEmit0 errors. Every file is TypeScript. Strict mode is on. Every component, every hook, every API call is fully typed. Autocomplete works everywhere. Entire classes of bugs are now impossible.