# 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<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

```typescript
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

```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!
