TypeScript Tips die Elke Developer Zou Moeten Kennen

Praktische TypeScript tips en tricks om je code robuuster, leesbaarder en makkelijker te onderhouden te maken.

Jean-Pierre Broeders

Freelance DevOps Engineer

18 februari 20265 min. leestijd
TypeScript Tips die Elke Developer Zou Moeten Kennen

TypeScript is inmiddels de standaard geworden in veel projecten — en terecht. Maar de taal heeft meer in huis dan alleen string en number. In dit artikel deel ik concrete tips die je dag-tot-dag TypeScript werk een stuk aangenamer maken.

1. Gebruik satisfies in Plaats van Type Assertions

Vanaf TypeScript 4.9 heb je de satisfies operator. Die geeft je het beste van twee werelden: type-checking én automatisch type-inference.

const config = {
  port: 3000,
  host: "localhost",
  debug: true,
} satisfies Record<string, string | number | boolean>;

// TypeScript weet nu dat config.port een number is — niet string | number | boolean
console.log(config.port.toFixed(0)); // ✅ werkt

Met een gewone type annotatie (: Record<string, ...>) zou je config.port als union type zien en verlies je de specifieke inferentie.

2. Template Literal Types voor Veilige String Patronen

Je kunt string patronen nu sterk typeren:

type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiRoute = `/api/${string}`;
type Endpoint = `${HttpMethod} ${ApiRoute}`;

function callApi(endpoint: Endpoint): Promise<Response> {
  const [method, url] = endpoint.split(" ", 2);
  return fetch(url, { method });
}

callApi("GET /api/users");       // ✅
callApi("PATCH /api/users");     // ❌ Type error
callApi("GET users");            // ❌ Type error

Dit vangt fouten op compile-time op die anders pas in productie opduiken.

3. Discriminated Unions: Betere Error Handling

Stop met any als return type bij functies die kunnen falen. Gebruik een discriminated union:

type Result<T> =
  | { success: true; data: T }
  | { success: false; error: string };

async function fetchUser(id: string): Promise<Result<User>> {
  try {
    const user = await db.users.findById(id);
    if (!user) return { success: false, error: "Gebruiker niet gevonden" };
    return { success: true, data: user };
  } catch (e) {
    return { success: false, error: "Database fout" };
  }
}

// Gebruik:
const result = await fetchUser("123");
if (result.success) {
  console.log(result.data.name); // TypeScript weet dat data hier bestaat
} else {
  console.error(result.error);   // En dat error hier bestaat
}

4. const Assertions voor Onveranderlijke Data

Wil je dat een object of array volledig als readonly en literal type wordt behandeld?

const STATUSES = ["pending", "active", "archived"] as const;
type Status = typeof STATUSES[number]; // "pending" | "active" | "archived"

const DEFAULT_CONFIG = {
  retries: 3,
  timeout: 5000,
  strategy: "exponential",
} as const;

Zonder as const zou Status gewoon string zijn. Met as const krijg je exacte literal types — en je kunt de array nooit per ongeluk muteren.

5. Utility Types Slim Inzetten

TypeScript heeft een rijke standaardbibliotheek van utility types. Gebruik ze:

interface User {
  id: string;
  name: string;
  email: string;
  role: "admin" | "user";
  createdAt: Date;
}

// Alleen bepaalde velden verplicht bij aanmaken
type CreateUserDto = Pick<User, "name" | "email" | "role">;

// Alles optioneel voor updates
type UpdateUserDto = Partial<Pick<User, "name" | "email">>;

// Alles readonly voor view-only contexts
type UserView = Readonly<User>;

// Zonder gevoelige velden
type PublicUser = Omit<User, "email" | "role">;

Dit bespaart je een hoop dubbele type-definities.

6. Generics met Constraints

Generics worden pas echt krachtig als je constraints toevoegt:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: "JP", age: 30, active: true };
const name = getProperty(user, "name");   // type: string ✅
const age  = getProperty(user, "age");    // type: number ✅
getProperty(user, "missing");             // ❌ compile error

7. Type Guards voor Runtime Veiligheid

TypeScript types bestaan alleen op compile-time. Als je data van buiten verwerkt (API, JSON), heb je runtime checks nodig:

interface ApiUser {
  id: string;
  name: string;
  email: string;
}

function isApiUser(data: unknown): data is ApiUser {
  return (
    typeof data === "object" &&
    data !== null &&
    typeof (data as ApiUser).id === "string" &&
    typeof (data as ApiUser).name === "string" &&
    typeof (data as ApiUser).email === "string"
  );
}

const raw = await response.json();
if (isApiUser(raw)) {
  console.log(raw.name); // ✅ TypeScript vertrouwt het nu
}

Conclusie

TypeScript is meer dan een type-checker op JavaScript. Met satisfies, discriminated unions, template literal types en slimme utility types schrijf je code die minder bugs bevat en makkelijker te lezen en refactoren is. Begin met één tip die je nog niet gebruikte en bouw van daar verder.

Goede TypeScript is geen kunst — het is een gewoonte.

Veelgestelde vragen over TypeScript

Wat is het verschil tussen satisfies en een type-annotatie? Een type-annotatie (: Type) verbreedt de inferentie naar dat type, waardoor je specifieke informatie verliest. satisfies controleert dat je waarde aan het type voldoet, maar behoudt de smalle, letterlijke inferentie. Je krijgt dus type-checking én precieze typen.

Wanneer gebruik ik een discriminated union? Gebruik discriminated unions wanneer een waarde meerdere vormen kan hebben — bijvoorbeeld een resultaat dat slaagt of faalt. Een gedeeld discriminerend veld (zoals success: true | false) laat TypeScript automatisch de juiste tak afleiden, wat veiliger is dan any of losse null-checks.

Heb ik nog runtime-checks nodig met TypeScript? Ja. TypeScript-typen verdwijnen bij compilatie en bieden geen garanties over data van buiten (API's, JSON, formulieren). Gebruik type guards (data is Type) of een schema-validator om binnenkomende data op runtime te valideren.

Wat doet as const? as const markeert een waarde als volledig readonly en behoudt de letterlijke typen. Zo wordt ["a","b"] as const het type readonly ["a","b"] in plaats van string[] — ideaal om unions af te leiden uit constante arrays.

Verder lezen

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op