Files
novi-lessons/Les04-TypeScript-Fundamentals/Les04-Escaperoom-ANTWOORDEN.md
2026-03-02 14:45:39 +01:00

15 KiB

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

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

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

let gameTitle: string = "Elden Ring";
let playerName = "Tarnished";
function processScores(scores: number[], callback) {
function calculate(a: number, b) {

De fix

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:

const user1: User = { ... }
const product1: Product = { ... }
const employee: Employee = { ... }

De fix

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

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

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

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

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

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

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

function transformArray<T, U>(items: T[], callback: Transformer<T, U>): 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<T>(items: T[], predicate: Transformer<T, boolean>): T[] {

De fix

type Validator = (value: string) => boolean;
type Transformer<T, U> = (item: T) => U;
type EventHandler = (event: unknown) => void;

function transformArray<T, U>(items: T[], callback: Transformer<T, U>): 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<T>(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<T, boolean>.

Room Code

FREE

Kamer 8: Boss Room

Wat is kapot

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

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!