Module Recap
Module 05 gave you the tools to write reusable, fully typed code. The signature feature — generic type parameters — lets a single function or interface work correctly with any type the caller provides, without sacrificing the type safety that makes TypeScript worth using.
What you learned
Section titled “What you learned”What generics are — a type parameter is a placeholder that the caller fills in. The type flows through the function without unsafe casts. Array<T> and Promise<T> are the generic types you have used throughout the course.
Generic functions — function fn<T> declares T as a type parameter. TypeScript infers T from call-site arguments when possible. fetchJson<T> and shuffle<T> replaced their non-generic versions with functions that work correctly for any type.
Generic interfaces and type aliases — interface Shape<T> and type Alias<T> parameterize the shape itself. Nullable<T>, Handler<T>, and LoadingState<T> are reusable patterns built on this.
Generic constraints — T extends SomeType restricts what T can be, allowing the function body to access properties guaranteed by the constraint. K extends keyof T restricts K to known property names.
Utility types — Partial, Required, Readonly, Pick, Omit, and Record transform existing types systematically. They compose — the output of one is a valid input to another.
The final api.ts
Section titled “The final api.ts”With generics in place, api.ts is fully typed with no unsafe casts at call sites:
import type { ApiResponse, RawQuestion, TriviaQuestion, Difficulty } from './types.js';import { ResponseCode } from './types.js';
const BASE_URL = 'https://opentdb.com/api.php';
export async function fetchJson<T>(url: string): Promise<T> { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`); return response.json() as Promise<T>;}
function decodeHtml(html: string): string { const el = document.createElement('textarea'); el.innerHTML = html; return el.value;}
function shuffle<T>(arr: T[]): T[] { return [...arr].sort(() => Math.random() - 0.5);}
function normalizeQuestion(raw: RawQuestion): TriviaQuestion { return { question: decodeHtml(raw.question), category: decodeHtml(raw.category), difficulty: raw.difficulty as Difficulty, correctAnswer: decodeHtml(raw.correct_answer), allAnswers: shuffle([raw.correct_answer, ...raw.incorrect_answers]).map(decodeHtml), };}
export async function fetchQuestions( amount: number = 10, difficulty?: Difficulty,): Promise<TriviaQuestion[]> { let url = `${BASE_URL}?amount=${amount}&type=multiple`; if (difficulty) url += `&difficulty=${difficulty}`;
const data = await fetchJson<ApiResponse>(url);
if (data.response_code === ResponseCode.NoResults) { throw new Error('Not enough questions available. Try a different setting.'); } if (data.response_code !== ResponseCode.Success) { throw new Error(`API error (code ${data.response_code})`); }
return data.results.map(normalizeQuestion);}fetchJson<ApiResponse> returns Promise<ApiResponse> — data is fully typed. data.results is RawQuestion[]. data.response_code is number. No casts downstream.
Key terms
Section titled “Key terms”| Term | What it means |
|---|---|
| Type parameter | A placeholder type declared with <T> that the caller fills in |
| Generic function | A function with one or more type parameters |
| Type inference | TypeScript inferring T from the call-site argument — no explicit supply needed |
| Generic constraint | T extends SomeType — restricts T to types with at least the required shape |
keyof T | A union of the property names of T |
| Indexed access type | T[K] — the type of property K on type T |
| Utility type | A built-in generic type transformation: Partial, Pick, Omit, Record, etc. |
What is next
Section titled “What is next”Module 06 covers type narrowing — the mechanism TypeScript uses to move from a broad type to a specific one inside a conditional branch. You will narrow union types with typeof and instanceof, use the in operator for property checks, build discriminated unions, write type predicates, and understand unknown, any, and never. AceIt uses all of these to safely handle the API response and model quiz state.