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:
- Kamer 1 (Basic Types): TYPE
- Kamer 2 (Type Inference): SAFE
- Kamer 3 (Interfaces): CODE
- Kamer 4 (Optional Properties): LOCK
- Kamer 5 (Union Types): OPEN
- Kamer 6 (Type Aliases): DOOR
- Kamer 7 (Function Types): FREE
- 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!