typescript / expert
Snippet
Typsichere Test-Doubles mit dem satisfies-Operator
Der 'satisfies'-Operator (TS 4.9+) ist das richtige Werkzeug für Test-Doubles. Anders als eine Typannotation (': UserRepo') verbreitert er die Variable nicht auf das Interface — findById behält seinen engeren Non-Nullable-Rückgabetyp, sodass folgender Code .name.toUpperCase() ohne Null-Check aufrufen kann. Anders als 'as UserRepo' prüft er weiterhin, dass das Objekt den Vertrag vollständig erfüllt; eine fehlende Methode oder falsche Rückgabeform löst einen Kompilierfehler aus. Für Test-Doubles bedeutet das volle IntelliSense auf Zusatzfeldern (Call-Recorder, Spies) bei gleichzeitigem Schutz gegen Drift, wenn das Produktions-Interface sich ändert.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface UserRepo {findById(id: string): Promise<{ id: string; name: string } | null>;save(user: { id: string; name: string }): Promise<void>;}const calls: string[] = [];const mockRepo = {findById: async (id: string) => ({ id, name: "Stub" }),save: async (u: { id: string; name: string }) => {calls.push(u.id);},} satisfies UserRepo;// 'satisfies' verifies conformance without widening the value:const user = await mockRepo.findById("u1");console.log(user.name.toUpperCase()); // name stays string, not string | null
Erklärung
1
} satisfies UserRepo;
Validiert die Konformität zu UserRepo, behält aber den engeren, literalen inferierten Typ des Objekts.
2
findById: async (id: string) => ({ id, name: "Stub" }),
Inferierter Rückgabetyp ist Promise<{ id: string; name: string }> — enger als die Nullable-Union des Interfaces.
3
const user = await mockRepo.findById("u1");
Da der inferierte Typ nicht verbreitert wird, ist 'user' hier non-nullable — kein defensiver Null-Check nötig.
4
console.log(user.name.toUpperCase());
Kompiliert sauber; mit ': UserRepo' statt satisfies wäre eine Verengung nötig.