fix: update les 4
This commit is contained in:
@@ -977,31 +977,28 @@ _[Pauzeer. Maak oogcontact.]_
|
|||||||
|
|
||||||
"Huiswerk voor volgende week:"
|
"Huiswerk voor volgende week:"
|
||||||
|
|
||||||
_[Schrijf op en stuur ook via email/Canvas:]_
|
_[Schrijf op en stuur ook via email/Teams:]_
|
||||||
|
|
||||||
"**Huiswerk Les 4:**
|
"**Huiswerk Les 4:**
|
||||||
|
|
||||||
1. **Finish all 8 TypeScript Escaperoom rooms** (if not done today)
|
1. **Download `les4-huiswerk-js-converter.zip`** van Teams
|
||||||
2. **Download de JS→TS Converter zip** van Canvas
|
2. Je krijgt 4 JavaScript bestanden: users, products, orders, utils
|
||||||
- Convert 3-5 JavaScript files to TypeScript
|
3. Zet ze allemaal om naar TypeScript — interfaces schrijven, union types, functies typen
|
||||||
- Voeg types toe aan alle functies en variabelen
|
4. De tests staan al in TypeScript — die zijn je hints!
|
||||||
- Zorg dat TypeScript compiler 0 errors geeft
|
5. `npm run check` moet 0 errors geven, `npm test` moet groen zijn
|
||||||
3. **Prepare for Les 5** — TypeScript + React
|
6. **Geen `any`!** Als ik `any` zie, stuur ik het terug.
|
||||||
- Denk: hoe zouden je React props getypeerd worden?
|
|
||||||
|
|
||||||
Room 1-8 van de escaperoom moet af. Als je niet klaar bent vandaag, je hebt de week om het af te maken. Volgende week bouw je hierop voort."
|
En als je de escaperoom nog niet af hebt: maak die ook af."
|
||||||
|
|
||||||
_[Toon Slide 22 preview.]_
|
_[Toon Slide 22 preview.]_
|
||||||
|
|
||||||
"**Volgende week: Les 5. TypeScript + React.**"
|
"**Volgende week: Les 5. TypeScript voor React.**"
|
||||||
|
|
||||||
"Jullie weten nu: types voorkomen fouten. Functies typen. Interfaces. Volgende week: React components. Props zijn... functies. Dus: props typen. useState? Het returned state EN een setter — beide getypd. Events? Handlers getypd."
|
"Jullie weten nu de basis: types, interfaces, union types, functies typen. Volgende week pakken we het volgende level: hoe gebruik je TypeScript in React? Props typen, useState met types, event handlers, API calls. Dat is de laatste stap voordat we in Les 6 met Next.js beginnen."
|
||||||
|
|
||||||
"**TypeScript + React = superkracht.** Cursor snapt je code beter. Minder bugs. Betere autocomplete."
|
|
||||||
|
|
||||||
_[Maak oogcontact, glimlach.]_
|
_[Maak oogcontact, glimlach.]_
|
||||||
|
|
||||||
"Jullie hebben vandaag veel geleerd. TypeScript lijkt misschien veel regels. Maar denk eraan: elke rode squiggle is TypeScript die zegt: 'Hé, ik help je.' Niet iets wat boos is. Het is helpful."
|
"Jullie hebben vandaag veel geleerd. TypeScript lijkt misschien veel regels. Maar denk eraan: elke rode squiggle is TypeScript die zegt: 'Hé, ik help je.' Het is niet boos — het is helpful."
|
||||||
|
|
||||||
"Goed gedaan vandaag. Tot volgende keer!"
|
"Goed gedaan vandaag. Tot volgende keer!"
|
||||||
|
|
||||||
|
|||||||
@@ -709,31 +709,28 @@ En oefening. Je bent nooit klaar met TypeScript. Er is altijd meer om te leren.
|
|||||||
|
|
||||||
### Op de Slide
|
### Op de Slide
|
||||||
**Huiswerk (les 4):**
|
**Huiswerk (les 4):**
|
||||||
- Finaliseer zoveel escaperoom kamers als je kan
|
- Download `les4-huiswerk-js-converter.zip` van Teams
|
||||||
- Verzend je TypeScript file naar me (geen `any`!)
|
- Zet 4 JavaScript bestanden om naar TypeScript
|
||||||
- Lees het TypeScript Handbook intro (5 minuten)
|
- Schrijf interfaces, union types, typed functies
|
||||||
|
- `npm run check` = 0 errors, `npm test` = groen
|
||||||
|
- Geen `any` toegestaan!
|
||||||
|
|
||||||
**Preview les 5 - Next.js Basics:**
|
**Preview les 5 - TypeScript voor React:**
|
||||||
- Next.js setup
|
- Generics & utility types
|
||||||
- File-based routing
|
- Props & state typen in React
|
||||||
- Eerste pagina's maken
|
- Event handlers & async functies typen
|
||||||
- TypeScript in Next.js
|
- API responses typen met `Promise<T>`
|
||||||
- Deploy naar Vercel
|
|
||||||
|
|
||||||
Visual: Next.js logo met pijl naar "Les 5"
|
Visual: TypeScript logo → React logo → "Les 5"
|
||||||
|
|
||||||
### Docentnotities
|
### Docentnotities
|
||||||
"Huiswerk: finaliseer die escaperoom. Verzend je file naar me. Ik wil geen `any` zien, anders stuur ik het terug!
|
"Huiswerk: jullie krijgen een JavaScript project met 4 bestanden — users, products, orders, utils. Alles moet omgezet worden naar TypeScript. Interfaces schrijven, union types gebruiken, functies typen. De tests staan al in TypeScript, die vertellen je wat de verwachte types zijn. `npm run check` moet 0 errors geven en `npm test` moet groen zijn. Geen `any`!
|
||||||
|
|
||||||
En lees het TypeScript Handbook intro. Niet heel veel tijd, maar het helpt je context.
|
Volgende les gaan we verder met TypeScript, maar dan voor React. Hoe type je props? Hoe werkt useState met types? Hoe type je een API call? Dat is de brug naar Les 6, waar we beginnen met Next.js.
|
||||||
|
|
||||||
Volgende les? NEXT.JS. Dit is waar het echt spannend wordt. Jullie gaan echte web apps bouwen met React, TypeScript, en all the bells and whistles.
|
Dus zorg dat je huiswerk doet — je hebt die basis nodig.
|
||||||
|
|
||||||
Next.js is het framework dat everyone uses. Vercel (de makers) gebruiken het. Jullie gaan het morgen gebruiken.
|
Goed werk vandaag! Tot volgende keer!"
|
||||||
|
|
||||||
Dus zorg dat je huiswerk doet. Het volgende is big.
|
|
||||||
|
|
||||||
Goed werk vandaag! Ik ben trots op jullie. Tot volgende keer!"
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,393 +1,234 @@
|
|||||||
# Les 5: TypeScript Basics
|
# Les 5: TypeScript voor React
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Hoofdstuk
|
## Hoofdstuk
|
||||||
**Deel 2: Technical Foundations** (Les 5-9)
|
**Deel 2: Technical Foundations** (Les 4-9)
|
||||||
|
|
||||||
## Beschrijving
|
## Beschrijving
|
||||||
Introductie tot TypeScript voor React developers. Leer waarom TypeScript waardevol is, hoe je types schrijft, en hoe je het combineert met React.
|
Verdieping in TypeScript met focus op React-patronen. Studenten leren generics, utility types, en hoe je React components, hooks, events en API calls correct typt. Voorbereiding op Les 6 waar ze met Next.js aan de slag gaan.
|
||||||
|
|
||||||
|
**Voorkennis:** Les 4 (TypeScript Fundamentals) — basic types, interfaces, union types, type aliases, functies typen.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Te Behandelen
|
## Te Behandelen
|
||||||
|
|
||||||
### Waarom TypeScript?
|
### Generics (20 min)
|
||||||
|
- Waarom generics? Herbruikbare, type-safe code
|
||||||
**Het probleem met JavaScript:**
|
- `Array<T>`, `Promise<T>` — generics die ze al kennen
|
||||||
```javascript
|
- Eigen generics schrijven: `function getFirst<T>(items: T[]): T`
|
||||||
function greet(name) {
|
- Generics met constraints: `<T extends { id: string }>`
|
||||||
return "Hello, " + name.toUpperCase();
|
- `keyof` operator: `function getValue<T, K extends keyof T>(obj: T, key: K): T[K]`
|
||||||
}
|
|
||||||
|
|
||||||
greet(42); // Runtime error! 42.toUpperCase is not a function
|
|
||||||
```
|
|
||||||
|
|
||||||
**De oplossing met TypeScript:**
|
|
||||||
```typescript
|
|
||||||
function greet(name: string): string {
|
|
||||||
return "Hello, " + name.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
greet(42); // Compile error! Argument of type 'number' is not assignable to type 'string'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Voordelen:**
|
|
||||||
- Fouten vinden VOORDAT je code runt
|
|
||||||
- Betere autocomplete in je editor
|
|
||||||
- Code is zelf-documenterend
|
|
||||||
- AI tools begrijpen je code beter
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Basic Types
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Primitives
|
// Generic functie
|
||||||
let name: string = "Tim";
|
function wrapInArray<T>(value: T): T[] {
|
||||||
let age: number = 25;
|
return [value];
|
||||||
let isStudent: boolean = true;
|
}
|
||||||
|
|
||||||
// Arrays
|
wrapInArray("hello"); // string[]
|
||||||
let numbers: number[] = [1, 2, 3];
|
wrapInArray(42); // number[]
|
||||||
let names: string[] = ["Tim", "Anna"];
|
|
||||||
|
|
||||||
// Alternative array syntax
|
// Met constraint
|
||||||
let scores: Array<number> = [90, 85, 88];
|
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
|
||||||
|
return obj[key];
|
||||||
// Objects
|
}
|
||||||
let user: { name: string; age: number } = {
|
|
||||||
name: "Tim",
|
|
||||||
age: 25
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Type Inference
|
### Utility Types (15 min)
|
||||||
|
- `Partial<T>` — alle properties optioneel (handig voor updates)
|
||||||
TypeScript raadt types vaak zelf:
|
- `Pick<T, K>` — selecteer specifieke properties
|
||||||
|
- `Omit<T, K>` — alles behalve specifieke properties
|
||||||
```typescript
|
- `Record<K, V>` — key-value mapping
|
||||||
// TypeScript weet dat dit een string is
|
- Praktisch voorbeeld: `updateUser(id: string, data: Partial<User>)`
|
||||||
let message = "Hello"; // type: string
|
|
||||||
|
|
||||||
// TypeScript weet dat dit een number is
|
|
||||||
let count = 42; // type: number
|
|
||||||
|
|
||||||
// TypeScript weet wat de functie returned
|
|
||||||
function double(x: number) {
|
|
||||||
return x * 2; // return type: number (inferred)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Regel:** Je hoeft niet altijd types te schrijven. Laat TypeScript inferren waar mogelijk.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Interfaces
|
|
||||||
|
|
||||||
Voor het beschrijven van object shapes:
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface User {
|
interface User {
|
||||||
id: number;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
isActive: boolean;
|
age: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user: User = {
|
// Partial: voor update functies
|
||||||
id: 1,
|
function updateUser(id: string, updates: Partial<User>): User { ... }
|
||||||
name: "Tim",
|
updateUser("1", { name: "Tim" }); // alleen name updaten
|
||||||
email: "tim@example.com",
|
|
||||||
isActive: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional properties met ?
|
// Omit: voor create functies (id wordt server-side gegenereerd)
|
||||||
interface Product {
|
type CreateUserInput = Omit<User, "id">;
|
||||||
id: number;
|
|
||||||
name: string;
|
// Pick: voor specifieke views
|
||||||
price: number;
|
type UserPreview = Pick<User, "id" | "name">;
|
||||||
description?: string; // optioneel
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Type Aliases
|
### React Props Typen (20 min)
|
||||||
|
- Interface voor component props
|
||||||
Alternatief voor interfaces, meer flexibel:
|
- Children typen met `React.ReactNode`
|
||||||
|
- Callback props: `onClick: () => void`, `onChange: (value: string) => void`
|
||||||
|
- Spread props en prop forwarding
|
||||||
|
- Default values met destructuring
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Type alias voor object
|
interface CardProps {
|
||||||
type User = {
|
title: string;
|
||||||
id: number;
|
children: React.ReactNode;
|
||||||
name: string;
|
variant?: "default" | "highlighted";
|
||||||
};
|
onClose?: () => void;
|
||||||
|
|
||||||
// Type alias voor union types
|
|
||||||
type Status = "pending" | "approved" | "rejected";
|
|
||||||
|
|
||||||
// Type alias voor functie
|
|
||||||
type GreetFunction = (name: string) => string;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Interface vs Type:**
|
|
||||||
- Interface: voor objecten, kan extended worden
|
|
||||||
- Type: voor alles, meer flexibel
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### TypeScript met React
|
|
||||||
|
|
||||||
**Props typen:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Interface voor props
|
|
||||||
interface ButtonProps {
|
|
||||||
label: string;
|
|
||||||
onClick: () => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Component met typed props
|
function Card({ title, children, variant = "default", onClose }: CardProps) {
|
||||||
function Button({ label, onClick, disabled = false }: ButtonProps) {
|
|
||||||
return (
|
return (
|
||||||
<button onClick={onClick} disabled={disabled}>
|
<div className={`card card-${variant}`}>
|
||||||
{label}
|
<h2>{title}</h2>
|
||||||
</button>
|
{onClose && <button onClick={onClose}>×</button>}
|
||||||
);
|
{children}
|
||||||
}
|
|
||||||
|
|
||||||
// Gebruik
|
|
||||||
<Button label="Click me" onClick={() => console.log("Clicked!")} />
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### useState met Types
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
// Type inference werkt vaak
|
|
||||||
const [count, setCount] = useState(0); // number
|
|
||||||
|
|
||||||
// Explicit type voor complexe data
|
|
||||||
interface User {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [user, setUser] = useState<User | null>(null);
|
|
||||||
|
|
||||||
// Array van objecten
|
|
||||||
const [users, setUsers] = useState<User[]>([]);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Generics Basics
|
|
||||||
|
|
||||||
Generics maken code herbruikbaar:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Array is een generic type
|
|
||||||
const numbers: Array<number> = [1, 2, 3];
|
|
||||||
const names: Array<string> = ["Tim", "Anna"];
|
|
||||||
|
|
||||||
// Promise is een generic type
|
|
||||||
async function fetchUser(): Promise<User> {
|
|
||||||
const response = await fetch('/api/user');
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Je kunt ook eigen generics maken
|
|
||||||
function firstElement<T>(arr: T[]): T | undefined {
|
|
||||||
return arr[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const first = firstElement([1, 2, 3]); // type: number | undefined
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Veelvoorkomende Errors
|
|
||||||
|
|
||||||
**Error 1: Type 'X' is not assignable to type 'Y'**
|
|
||||||
```typescript
|
|
||||||
let name: string = 42; // Error!
|
|
||||||
// Fix: gebruik correct type
|
|
||||||
let name: string = "Tim";
|
|
||||||
```
|
|
||||||
|
|
||||||
**Error 2: Property 'X' does not exist on type 'Y'**
|
|
||||||
```typescript
|
|
||||||
interface User { name: string; }
|
|
||||||
const user: User = { name: "Tim" };
|
|
||||||
console.log(user.age); // Error! 'age' bestaat niet
|
|
||||||
// Fix: voeg property toe aan interface
|
|
||||||
```
|
|
||||||
|
|
||||||
**Error 3: Object is possibly 'undefined'**
|
|
||||||
```typescript
|
|
||||||
const users: User[] = [];
|
|
||||||
console.log(users[0].name); // Error! users[0] kan undefined zijn
|
|
||||||
// Fix: check eerst
|
|
||||||
if (users[0]) {
|
|
||||||
console.log(users[0].name);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### JS naar TS Omzetten
|
|
||||||
|
|
||||||
**Stap 1:** Rename `.js` naar `.tsx` (voor React) of `.ts`
|
|
||||||
|
|
||||||
**Stap 2:** Fix de rode errors - meestal:
|
|
||||||
- Voeg types toe aan function parameters
|
|
||||||
- Maak interfaces voor objecten
|
|
||||||
- Handle nullable values
|
|
||||||
|
|
||||||
**Voorbeeld:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Voorheen (JavaScript)
|
|
||||||
function UserCard({ user }) {
|
|
||||||
return <div>{user.name}</div>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Nu (TypeScript)
|
|
||||||
interface User {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UserCardProps {
|
|
||||||
user: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
function UserCard({ user }: UserCardProps) {
|
|
||||||
return <div>{user.name}</div>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
- OpenCode/WebStorm
|
|
||||||
- TypeScript (via Next.js)
|
|
||||||
- React
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Lesopdracht (2 uur)
|
|
||||||
|
|
||||||
### TypeScript Hands-on
|
|
||||||
|
|
||||||
**Deel 1: JS naar TS Omzetten (45 min)**
|
|
||||||
|
|
||||||
Gegeven JavaScript component:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function ProductCard({ product, onAddToCart }) {
|
|
||||||
return (
|
|
||||||
<div className="p-4 border rounded">
|
|
||||||
<h2>{product.name}</h2>
|
|
||||||
<p>${product.price}</p>
|
|
||||||
{product.description && <p>{product.description}</p>}
|
|
||||||
<button onClick={() => onAddToCart(product.id)}>
|
|
||||||
Add to Cart
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Zet dit om naar TypeScript:
|
---
|
||||||
1. Maak `Product` interface
|
|
||||||
2. Maak `ProductCardProps` interface
|
|
||||||
3. Type de component
|
|
||||||
4. Fix alle TypeScript errors
|
|
||||||
|
|
||||||
**Deel 2: Interfaces Schrijven (30 min)**
|
### useState & useEffect Typen (15 min)
|
||||||
|
- Type inference bij useState: `useState(0)` → number
|
||||||
|
- Explicit types: `useState<User | null>(null)`
|
||||||
|
- Arrays: `useState<Product[]>([])`
|
||||||
|
- useEffect met async patterns
|
||||||
|
|
||||||
Maak interfaces voor:
|
```typescript
|
||||||
|
const [user, setUser] = useState<User | null>(null);
|
||||||
|
const [products, setProducts] = useState<Product[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false); // inference: boolean
|
||||||
|
|
||||||
1. **User** met: id, name, email, avatar (optioneel), createdAt
|
useEffect(() => {
|
||||||
2. **Product** met: id, name, price, description (optioneel), inStock, category
|
async function fetchData() {
|
||||||
3. **Order** met: id, userId, products (array), total, status (pending/shipped/delivered)
|
setLoading(true);
|
||||||
|
const response = await fetch("/api/users");
|
||||||
|
const data: User[] = await response.json();
|
||||||
|
setUsers(data);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
**Deel 3: React Component met Types (45 min)**
|
---
|
||||||
|
|
||||||
Bouw een `UserList` component:
|
### Event Handlers Typen (10 min)
|
||||||
- Props: users array, onSelectUser callback
|
- `React.ChangeEvent<HTMLInputElement>`
|
||||||
- State: selectedUserId (number of null)
|
- `React.FormEvent<HTMLFormElement>`
|
||||||
- Toon lijst van users, highlight geselecteerde
|
- `React.MouseEvent<HTMLButtonElement>`
|
||||||
|
- Tip: hover in Cursor om het juiste event type te vinden
|
||||||
|
|
||||||
Alle types moeten correct zijn. Geen `any` gebruiken!
|
```typescript
|
||||||
|
function SearchForm() {
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
|
||||||
### Deliverable
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
- ProductCard.tsx met correcte types
|
setQuery(e.target.value);
|
||||||
- types.ts met alle interfaces
|
};
|
||||||
- UserList.tsx volledig getypt
|
|
||||||
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("Searching:", query);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<input value={query} onChange={handleChange} />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### API Responses & Async Typen (15 min)
|
||||||
|
- `Promise<T>` voor async functies
|
||||||
|
- API response types definiëren
|
||||||
|
- Error handling met types
|
||||||
|
- Fetch wrapper met generics
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ApiResponse<T> {
|
||||||
|
data: T;
|
||||||
|
status: number;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gebruik
|
||||||
|
const { data: users } = await fetchApi<User[]>("/api/users");
|
||||||
|
const { data: product } = await fetchApi<Product>("/api/products/1");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
- Cursor (Student Plan)
|
||||||
|
- TypeScript
|
||||||
|
- React (via CDN of Vite)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lesopdracht (75 min)
|
||||||
|
|
||||||
|
### Typed React Dashboard
|
||||||
|
|
||||||
|
Studenten bouwen een kleine React-app met volledig getypte components:
|
||||||
|
|
||||||
|
**Opdracht:** Bouw een Product Dashboard met:
|
||||||
|
|
||||||
|
1. **`ProductCard` component** — props: Product interface, onAddToCart callback
|
||||||
|
2. **`ProductList` component** — props: Product[], filterCategory (union type)
|
||||||
|
3. **`SearchBar` component** — props: query string, onChange handler (getypt event)
|
||||||
|
4. **`useProducts` custom hook** — fetch products, return `{ products, loading, error }`
|
||||||
|
5. **Alle types in een apart `types.ts` bestand**
|
||||||
|
|
||||||
|
**Vereisten:**
|
||||||
|
- Geen `any` toegestaan
|
||||||
|
- Alle event handlers correct getypt
|
||||||
|
- useState met expliciete types waar nodig
|
||||||
|
- Minstens 1 generic functie (bijv. een `sortBy<T>` of `filterBy<T>`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Huiswerk (2 uur)
|
## Huiswerk (2 uur)
|
||||||
|
|
||||||
### TypeScript Verdieping
|
### Extend het Dashboard
|
||||||
|
|
||||||
**Deel 1: Drie Components Bouwen (1 uur)**
|
Bouw voort op de lesopdracht:
|
||||||
|
|
||||||
Bouw volledig in TypeScript:
|
1. **Shopping Cart** toevoegen met getypte state (`CartItem[]`)
|
||||||
|
2. **API simulatie** — maak een `fetchProducts()` functie met `Promise<Product[]>`
|
||||||
1. **SearchInput** component
|
3. **Utility types gebruiken** — `Partial<Product>` voor updates, `Omit<Product, "id">` voor create
|
||||||
- Props: value, onChange, placeholder (optioneel)
|
4. **Bonus: Generic `DataTable<T>` component** — werkt met elke array van objecten
|
||||||
- Volledig getypt
|
|
||||||
|
|
||||||
2. **DataTable** component
|
|
||||||
- Generic: werkt met elk type data
|
|
||||||
- Props: data array, columns config
|
|
||||||
- Type-safe rendering
|
|
||||||
|
|
||||||
3. **Modal** component
|
|
||||||
- Props: isOpen, onClose, title, children
|
|
||||||
- Correct gebruik van React.ReactNode
|
|
||||||
|
|
||||||
**Deel 2: Eindproject Interfaces (30 min)**
|
|
||||||
|
|
||||||
Bedenk de data structuur voor je eindproject:
|
|
||||||
- Welke entiteiten heb je? (users, posts, products, etc.)
|
|
||||||
- Maak interface voor elke entiteit
|
|
||||||
- Documenteer relaties tussen entiteiten
|
|
||||||
|
|
||||||
**Deel 3: Cheat Sheet (30 min)**
|
|
||||||
|
|
||||||
Maak persoonlijke TypeScript cheat sheet:
|
|
||||||
- Meest gebruikte types
|
|
||||||
- Interface vs Type wanneer
|
|
||||||
- Common patterns met React
|
|
||||||
- Hoe je errors oplost
|
|
||||||
|
|
||||||
### Deliverable
|
### Deliverable
|
||||||
- 3 TypeScript components
|
- Werkend React project met TypeScript
|
||||||
- types/index.ts met eindproject interfaces
|
- Alle components volledig getypt
|
||||||
- TypeScript cheat sheet (1 pagina)
|
- `npm run check` = 0 errors
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Leerdoelen
|
## Leerdoelen
|
||||||
Na deze les kan de student:
|
Na deze les kan de student:
|
||||||
- Uitleggen waarom TypeScript waardevol is
|
- Generics schrijven en toepassen
|
||||||
- Basic types gebruiken (string, number, boolean, arrays)
|
- Utility types gebruiken (Partial, Pick, Omit, Record)
|
||||||
- Interfaces en type aliases schrijven
|
- React component props correct typen
|
||||||
- React components typen met props
|
- useState en useEffect met types gebruiken
|
||||||
- useState met types gebruiken
|
- Event handlers typen (ChangeEvent, FormEvent, MouseEvent)
|
||||||
- Generics op basisniveau begrijpen
|
- Async functies en API responses typen met Promise<T>
|
||||||
- JavaScript code omzetten naar TypeScript
|
- Een custom hook schrijven met correcte return types
|
||||||
- TypeScript errors lezen en oplossen
|
|
||||||
|
|||||||
Reference in New Issue
Block a user