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