# Les04 TypeScript Escape Room - Antwoordsleutel **Moeilijkheid:** Easy → Hard **Totale tijd:** ~2-3 uur **Finale Escape Code:** `TYPE-SAFE-CODE-LOCK-OPEN-DOOR-FREE-DOM!` --- ## Kamer 1: Basic Types ### Wat is kapot ```typescript let filmTitle: number = "The Matrix"; let releaseYear: string = 1999; let isClassic: string = true; let scores: string[] = [8.7, 9.0, 8.5]; let cast: number[] = ["Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss"]; function calculateAverage(numbers: string[]): string { function printFilmInfo(title: string, year: number, rating: number) { ``` ### De fix ```typescript let filmTitle: string = "The Matrix"; let releaseYear: number = 1999; let isClassic: boolean = true; let scores: number[] = [8.7, 9.0, 8.5]; let cast: string[] = ["Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss"]; function calculateAverage(numbers: number[]): number { let total = 0; for (const number of numbers) { total += number; } return total / numbers.length; } function printFilmInfo(title: string, year: number, rating: number): void { console.log(`${title} (${year}) - Rating: ${rating}`); } ``` ### Uitleg De variabelen hebben verkeerde type annotaties toegewezen. `filmTitle` moet `string` zijn (niet `number`), `releaseYear` moet `number` zijn (niet `string`), `isClassic` moet `boolean` zijn (niet `string`), `scores` moet `number[]` zijn (niet `string[]`), en `cast` moet `string[]` zijn (niet `number[]`). De `calculateAverage` functie moet `number[]` accepteren en `number` retourneren. De `printFilmInfo` functie mist de return type annotation `: void`. ### Room Code ``` TYPE ``` --- ## Kamer 2: Type Inference ### Wat is kapot ```typescript let gameTitle: string = "Elden Ring"; let playerName = "Tarnished"; function processScores(scores: number[], callback) { function calculate(a: number, b) { ``` ### De fix ```typescript let gameTitle = "Elden Ring"; let playerName: string = "Tarnished"; function processScores(scores: number[], callback: (s: number) => number) { return scores.map(callback); } function calculate(a: number, b: number): number { return a + b; } ``` ### Uitleg Kamer 2 gaat om type inference en herkennen wanneer je type annotations nodig hebt. `gameTitle` heeft een expliciete type annotation `: string` die niet nodig is - TypeScript kan het afleiden uit de string literal. De `processScores` callback mist een type: het moet `(s: number) => number` zijn. De `calculate` parameter `b` mist een type (moet `number` zijn). Callback parameters MOETEN altijd expliciet getypeerd zijn, maar return types kunnen soms worden afgeleid. ### Room Code ``` SAFE ``` --- ## Kamer 3: Interfaces ### Wat is kapot De interfaces `User`, `Product`, `Address`, en `Employee` zijn niet gedefinieerd, maar worden wel gebruikt in type annotaties: ```typescript const user1: User = { ... } const product1: Product = { ... } const employee: Employee = { ... } ``` ### De fix ```typescript interface User { id: number; name: string; email: string; age: number; } interface Product { id: number; name: string; price: number; inStock: boolean; } interface Address { street: string; city: string; zipCode: string; } interface Employee { id: number; name: string; address: Address; salary: number; } const user1: User = { id: 1, name: "Alice", email: "alice@example.com", age: 28, }; const product1: Product = { id: 101, name: "Laptop", price: 999.99, inStock: true, }; const employee: Employee = { id: 42, name: "Bob", address: { street: "Main Street 123", city: "Amsterdam", zipCode: "1012 XY", }, salary: 50000, }; ``` ### Uitleg Interfaces moeten worden gedefinieerd voordat je ze als type annotations kunt gebruiken. `Address` is genest in `Employee`, dus moet als aparte interface worden gedefinieerd. Dit demonstreert object compositie: `Employee` bevat een `Address` object als property. ### Room Code ``` CODE ``` --- ## Kamer 4: Optional Properties ### Wat is kapot ```typescript interface Customer { id: number; name: string; email: string; phoneNumber: string; // Maar customer1 heeft dit niet! } interface Film { id: number; title: string; imdbScore: number; description: string; // Maar film1 heeft dit niet! releaseDate: number; // Maar film1 heeft dit niet! } function contactCustomer(customer: Customer): string { return `Call ${customer.name} at ${customer.phoneNumber}`; // Geen check of phoneNumber undefined is! } function printFilmDetails(film: Film): void { console.log(`${film.title} (${film.releaseDate})`); // Geen check of releaseDate undefined is! console.log(`Description: ${film.description}`); // Geen check of description undefined is! } ``` ### De fix ```typescript interface Customer { id: number; name: string; email: string; phoneNumber?: string; } interface Film { id: number; title: string; imdbScore: number; description?: string; releaseDate?: number; } function contactCustomer(customer: Customer): string { return `Call ${customer.name} at ${customer.phoneNumber || "no phone"}`; } function printFilmDetails(film: Film): void { console.log(`${film.title} (${film.releaseDate || "unknown year"})`); console.log(`Description: ${film.description || "No description"}`); console.log(`IMDB: ${film.imdbScore}`); } function safePrintCustomer(customer: Customer): void { let contact = `${customer.name} (${customer.email})`; if (customer.phoneNumber) { contact += ` - ${customer.phoneNumber}`; } console.log(contact); } function safeFilmInfo(film: Film): string { let info = `${film.title} - ${film.imdbScore}/10`; if (film.releaseDate) { info += ` (${film.releaseDate})`; } if (film.description) { info += ` - ${film.description}`; } return info; } ``` ### Uitleg De `?` markeren optionele properties (kunnen `undefined` zijn). Functies die optionele velden gebruiken, moeten controleren of ze bestaan voordat ze worden gebruikt. Zonder deze checks krijg je type errors. ### Room Code ``` LOCK ``` --- ## Kamer 5: Union Types ### Wat is kapot ```typescript const order1: Order = { id: 1, status: "processing", // Niet in union! amount: 99.99, paymentMethod: "paypal_pro", // Niet in union! }; const user1: User = { id: 1, name: "Carlos", theme: "system", // Niet in union! orders: [order1], }; ``` ### De fix ```typescript type OrderStatus = "pending" | "shipped" | "delivered"; type Theme = "light" | "dark" | "auto"; type PaymentMethod = "credit_card" | "paypal" | "bank_transfer"; const order1: Order = { id: 1, status: "shipped", // ✓ Geldige waarde amount: 99.99, paymentMethod: "paypal", // ✓ Geldige waarde }; const user1: User = { id: 1, name: "Carlos", theme: "dark", // ✓ Geldige waarde orders: [order1], }; ``` ### Uitleg Union types (`|`) beperken waarden tot specifieke opties. `status: "processing"` is geen geldige waarde uit de union. `paymentMethod: "paypal_pro"` is ook niet geldig (moet `"paypal"` zijn). `theme: "system"` is niet geldig (moet een van `"light" | "dark" | "auto"` zijn). De bestaande type aliases in het bestand zijn correct, dus je hoeft ze alleen maar in plaats van ongeldige waarden te gebruiken. ### Room Code ``` OPEN ``` --- ## Kamer 6: Type Aliases ### Wat is kapot ```typescript const developer1: Developer = { ... } // Developer type bestaat niet const developer2: Developer = { ... } const apiResponse1: ApiResponse = { ... } // ApiResponse type bestaat niet const apiResponse2: ApiResponse = { ... } const serverConfig: ServerConfig = { ... } // ServerConfig type bestaat niet callback: DeveloperCallback // DeveloperCallback type bestaat niet ``` ### De fix ```typescript type Developer = { id: number; name: string; email: string; programmingLanguages: string[]; yearsOfExperience: number; }; type ApiResponse = { success: boolean; data: any; message?: string; }; type ServerConfig = { hostname: string; port: number; ssl: boolean; timeout?: number; }; type DeveloperCallback = (dev: Developer) => void; const developer1: Developer = { id: 1, name: "Alice", email: "alice@dev.com", programmingLanguages: ["TypeScript", "JavaScript", "Python"], yearsOfExperience: 5, }; const developer2: Developer = { id: 2, name: "Bob", email: "bob@dev.com", programmingLanguages: ["Rust", "Go"], yearsOfExperience: 8, }; const apiResponse1: ApiResponse = { success: true, data: { count: 42 }, }; const apiResponse2: ApiResponse = { success: false, data: {}, message: "Not found", }; function createConfig(options: ServerConfig): void { console.log(`Connecting to ${options.hostname}:${options.port}`); } const serverConfig: ServerConfig = { hostname: "localhost", port: 3000, ssl: false, timeout: 5000, }; function processDevelopers( developers: Developer[], callback: DeveloperCallback ): void { developers.forEach(callback); } ``` ### Uitleg Type aliases (`type`) geven namen aan lange inline type definities. Dit volgt het DRY (Don't Repeat Yourself) principe. In plaats van dezelfde structuur steeds opnieuw in te typen, definieer je het eenmalig als `type Developer`. Dit maakt code leesbaar en gemakkelijk onderhoudbaar. ### Room Code ``` DOOR ``` --- ## Kamer 7: Function Types ### Wat is kapot ```typescript function transformArray(items: T[], callback: Transformer): U[] { function validateField(validators: Validation[], value: string) { // Return type ontbreekt! function createErrorHandler(callback: Validator): EventHandler { return (error: any) => { // error type is niet correct function createEventDispatcher(handlers: EventHandler[]): EventHandler { return (event: any) => { // event type is niet correct function filter(items: T[], predicate: Transformer): T[] { ``` ### De fix ```typescript type Validator = (value: string) => boolean; type Transformer = (item: T) => U; type EventHandler = (event: unknown) => void; function transformArray(items: T[], callback: Transformer): U[] { return items.map(callback); } function validateField(validators: Validation[], value: string): { valid: boolean; error?: string } { for (const validator of validators) { if (!validator.validator(value)) { return { valid: false, error: validator.errorMessage, }; } } return { valid: true, }; } function createErrorHandler(callback: (error: any) => void): EventHandler { return (error: unknown) => { console.log(`Error: ${error}`); if (typeof error === 'object' && error !== null && 'message' in error) { callback(error); } }; } function createEventDispatcher(handlers: EventHandler[]): EventHandler { return (event: unknown) => { handlers.forEach((handler) => handler(event)); }; } function filter(items: T[], predicate: (item: T) => boolean): T[] { return items.filter(predicate); } ``` ### Uitleg `validateField` mist de return type annotation (moet zijn `{ valid: boolean; error?: string }`). De `createErrorHandler` functie accepteert een callback maar die callback type klopt niet goed met hoe het wordt gebruikt. `createEventDispatcher` is een higher-order function (returnt een function) dus moet dat correct getypeerd zijn. De `filter` functie zou `(item: T) => boolean` als predicate moeten hebben, niet `Transformer`. ### Room Code ``` FREE ``` --- ## Kamer 8: Boss Room ### Wat is kapot ```typescript const movie1: Movie = { id: 1, title: "Inception", genre: "mysterious", // Niet in Genre union! releaseYear: 2010, director: nolan, status: "released", imdbScore: 8.8, reviews: [review1, review2], }; const movie2: Movie = { id: 2, title: "E.T.", genre: "sci-fi", releaseYear: 1982, director: spielberg, status: "complete", // Niet in MovieStatus union! reviews: [review1], }; ``` ### De fix ```typescript type Genre = "action" | "drama" | "sci-fi" | "comedy" | "horror"; type Rating = 1 | 2 | 3 | 4 | 5; type MovieStatus = "in_development" | "released" | "cancelled"; interface Director { id: number; name: string; films: string[]; } interface MovieReview { id: number; reviewer: string; rating: Rating; comment: string; date?: string; } interface Movie { id: number; title: string; genre: Genre; releaseYear: number; director: Director; status: MovieStatus; imdbScore?: number; reviews?: MovieReview[]; budget?: number; } type MovieLibrary = { movies: Movie[]; totalCount: number; lastUpdated?: string; }; const movie1: Movie = { id: 1, title: "Inception", genre: "sci-fi", // ✓ Geldige Genre releaseYear: 2010, director: nolan, status: "released", imdbScore: 8.8, reviews: [review1, review2], }; const movie2: Movie = { id: 2, title: "E.T.", genre: "sci-fi", releaseYear: 1982, director: spielberg, status: "released", // ✓ Geldige MovieStatus reviews: [review1], }; function filterMovies(movies: Movie[], predicate: (m: Movie) => boolean): Movie[] { return movies.filter(predicate); } function calculateAverageScore(movies: Movie[]): number { const scores = movies .filter((m: Movie) => m.imdbScore !== undefined) .map((m: Movie) => m.imdbScore as number); if (scores.length === 0) return 0; const total = scores.reduce((acc: number, score: number) => acc + score, 0); return total / scores.length; } function generateReport(library: MovieLibrary): string { return ( `Movies in library: ${library.movies.length}\n` + `Total: ${library.totalCount}\n` + `Last updated: ${library.lastUpdated || "unknown"}` ); } function findMoviesByDirector(movies: Movie[], director: Director): Movie[] { return movies.filter((m) => m.director.id === director.id); } function isHighQualityRelease(movie: Movie): boolean { return ( movie.status === "released" && movie.imdbScore !== undefined && movie.imdbScore >= 7 ); } ``` ### Uitleg Dit is het finale level! `genre: "mysterious"` is geen geldige `Genre` (moet één van `"action" | "drama" | "sci-fi" | "comedy" | "horror"` zijn). `status: "complete"` is geen geldige `MovieStatus` (moet één van `"in_development" | "released" | "cancelled"` zijn). Alle interfaces en type aliases moeten worden gedefinieerd. De functies moeten correct getypeerd zijn, inclusief proper handling van optionele `imdbScore` velden met undefined checks. ### Room Code ``` DOM! ``` --- ## ESCAPE CODE De complete escape code komt samen van alle kamer codes in volgorde: 1. **Kamer 1 (Basic Types):** TYPE 2. **Kamer 2 (Type Inference):** SAFE 3. **Kamer 3 (Interfaces):** CODE 4. **Kamer 4 (Optional Properties):** LOCK 5. **Kamer 5 (Union Types):** OPEN 6. **Kamer 6 (Type Aliases):** DOOR 7. **Kamer 7 (Function Types):** FREE 8. **Kamer 8 (Boss Room):** DOM! ``` TYPE-SAFE-CODE-LOCK-OPEN-DOOR-FREE-DOM! ``` Betekenis: "Type Safe Code Lock Open Door Freedom!" - Je code is veilig, je bent vrij van runtime errors!