fix: update les 4

This commit is contained in:
2026-03-03 13:33:10 +01:00
parent e8170ec2b4
commit d79449ea52
3 changed files with 198 additions and 363 deletions

View File

@@ -1,393 +1,234 @@
# Les 5: TypeScript Basics
# Les 5: TypeScript voor React
---
## Hoofdstuk
**Deel 2: Technical Foundations** (Les 5-9)
**Deel 2: Technical Foundations** (Les 4-9)
## 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
### Waarom TypeScript?
**Het probleem met JavaScript:**
```javascript
function greet(name) {
return "Hello, " + name.toUpperCase();
}
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
### Generics (20 min)
- Waarom generics? Herbruikbare, type-safe code
- `Array<T>`, `Promise<T>` — generics die ze al kennen
- Eigen generics schrijven: `function getFirst<T>(items: T[]): T`
- Generics met constraints: `<T extends { id: string }>`
- `keyof` operator: `function getValue<T, K extends keyof T>(obj: T, key: K): T[K]`
```typescript
// Primitives
let name: string = "Tim";
let age: number = 25;
let isStudent: boolean = true;
// Generic functie
function wrapInArray<T>(value: T): T[] {
return [value];
}
// Arrays
let numbers: number[] = [1, 2, 3];
let names: string[] = ["Tim", "Anna"];
wrapInArray("hello"); // string[]
wrapInArray(42); // number[]
// Alternative array syntax
let scores: Array<number> = [90, 85, 88];
// Objects
let user: { name: string; age: number } = {
name: "Tim",
age: 25
};
// Met constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
```
---
### Type Inference
TypeScript raadt types vaak zelf:
```typescript
// TypeScript weet dat dit een string is
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:
### Utility Types (15 min)
- `Partial<T>` — alle properties optioneel (handig voor updates)
- `Pick<T, K>` — selecteer specifieke properties
- `Omit<T, K>` — alles behalve specifieke properties
- `Record<K, V>` — key-value mapping
- Praktisch voorbeeld: `updateUser(id: string, data: Partial<User>)`
```typescript
interface User {
id: number;
id: string;
name: string;
email: string;
isActive: boolean;
age: number;
}
const user: User = {
id: 1,
name: "Tim",
email: "tim@example.com",
isActive: true
};
// Partial: voor update functies
function updateUser(id: string, updates: Partial<User>): User { ... }
updateUser("1", { name: "Tim" }); // alleen name updaten
// Optional properties met ?
interface Product {
id: number;
name: string;
price: number;
description?: string; // optioneel
}
// Omit: voor create functies (id wordt server-side gegenereerd)
type CreateUserInput = Omit<User, "id">;
// Pick: voor specifieke views
type UserPreview = Pick<User, "id" | "name">;
```
---
### Type Aliases
Alternatief voor interfaces, meer flexibel:
### React Props Typen (20 min)
- Interface voor component props
- Children typen met `React.ReactNode`
- Callback props: `onClick: () => void`, `onChange: (value: string) => void`
- Spread props en prop forwarding
- Default values met destructuring
```typescript
// Type alias voor object
type User = {
id: number;
name: string;
};
// 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;
interface CardProps {
title: string;
children: React.ReactNode;
variant?: "default" | "highlighted";
onClose?: () => void;
}
// Component met typed props
function Button({ label, onClick, disabled = false }: ButtonProps) {
function Card({ title, children, variant = "default", onClose }: CardProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
// 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 className={`card card-${variant}`}>
<h2>{title}</h2>
{onClose && <button onClick={onClose}>×</button>}
{children}
</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
2. **Product** met: id, name, price, description (optioneel), inStock, category
3. **Order** met: id, userId, products (array), total, status (pending/shipped/delivered)
useEffect(() => {
async function fetchData() {
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:
- Props: users array, onSelectUser callback
- State: selectedUserId (number of null)
- Toon lijst van users, highlight geselecteerde
### Event Handlers Typen (10 min)
- `React.ChangeEvent<HTMLInputElement>`
- `React.FormEvent<HTMLFormElement>`
- `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
- ProductCard.tsx met correcte types
- types.ts met alle interfaces
- UserList.tsx volledig getypt
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
};
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)
### TypeScript Verdieping
### Extend het Dashboard
**Deel 1: Drie Components Bouwen (1 uur)**
Bouw voort op de lesopdracht:
Bouw volledig in TypeScript:
1. **SearchInput** component
- Props: value, onChange, placeholder (optioneel)
- 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
1. **Shopping Cart** toevoegen met getypte state (`CartItem[]`)
2. **API simulatie** — maak een `fetchProducts()` functie met `Promise<Product[]>`
3. **Utility types gebruiken**`Partial<Product>` voor updates, `Omit<Product, "id">` voor create
4. **Bonus: Generic `DataTable<T>` component** — werkt met elke array van objecten
### Deliverable
- 3 TypeScript components
- types/index.ts met eindproject interfaces
- TypeScript cheat sheet (1 pagina)
- Werkend React project met TypeScript
- Alle components volledig getypt
- `npm run check` = 0 errors
---
## Leerdoelen
Na deze les kan de student:
- Uitleggen waarom TypeScript waardevol is
- Basic types gebruiken (string, number, boolean, arrays)
- Interfaces en type aliases schrijven
- React components typen met props
- useState met types gebruiken
- Generics op basisniveau begrijpen
- JavaScript code omzetten naar TypeScript
- TypeScript errors lezen en oplossen
- Generics schrijven en toepassen
- Utility types gebruiken (Partial, Pick, Omit, Record)
- React component props correct typen
- useState en useEffect met types gebruiken
- Event handlers typen (ChangeEvent, FormEvent, MouseEvent)
- Async functies en API responses typen met Promise<T>
- Een custom hook schrijven met correcte return types