typescript / expert
Snippet
Diskriminierte Promise-Ergebnisse mit `Promise.allSettled`-Narrowing
`Promise.all` ist fail-fast: ein Reject verwirft alle erfüllten Werte — falsch, sobald partieller Erfolg fachlich Sinn ergibt (Fan-out-Fetches, Batch-Writes, parallele Migrations). `Promise.allSettled` liefert pro Item eine diskriminierte Union, und das `'fulfilled' | 'rejected'`-Tag ist genau das, worauf TypeScript narrowt. Beachte `reason: unknown` — der Vertrag ist sauber: ein abgelehntes Promise kann mit allem reagieren, auch mit Nicht-Errors. Widerstehe `(reason as Error).message` und nutze stattdessen Type-Guards wie `reason instanceof Error`. Der Cast `as Settled<T>[]` ist die einzige akzeptable Stelle — der lib-Typ nutzt `any` für `reason`, eine Verschärfung auf `unknown` zieht die richtige Disziplin durch den Rest des Codes.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
type Settled<T> =| { status: 'fulfilled'; value: T }| { status: 'rejected'; reason: unknown };async function partition<T>(ps: ReadonlyArray<Promise<T>>): Promise<{ ok: T[]; failed: unknown[] }> {const results = await Promise.allSettled(ps);const ok: T[] = [];const failed: unknown[] = [];for (const r of results as Settled<T>[]) {if (r.status === 'fulfilled') {ok.push(r.value); // r is { status: 'fulfilled'; value: T }} else {failed.push(r.reason); // r is { status: 'rejected'; reason: unknown }}}return { ok, failed };}const { ok, failed } = await partition<number>([Promise.resolve(1),Promise.reject(new Error('boom')),Promise.resolve(2),]);// ok : number[] = [1, 2]// failed: unknown[] = [Error('boom')]
Erklärung
1
type Settled<T> = { status: 'fulfilled'; value: T } | { status: 'rejected'; reason: unknown };
Eigenhändig verschärfte Discriminated Union — ersetzt das `any` für `reason` aus der lib durch `unknown` und erzwingt sichere Fehlerprüfung weiter unten.
2
const results = await Promise.allSettled(ps);
Wartet auf jedes Input unabhängig vom Ausgang — kein vorzeitiges Abbrechen, jedes Promise meldet zurück.
3
if (r.status === 'fulfilled') { ok.push(r.value); }
Der literal-String-Diskriminator erlaubt TypeScript, `r` auf den Erfolgs-Zweig zu narrowen — `value: T` ist ohne Cast verfügbar.
4
failed.push(r.reason);
Bleibt absichtlich `unknown` — jeder Konsument muss per Type-Guard prüfen, bevor er es als `Error` behandelt.