`Promise.all` is fail-fast: one rejection discards every fulfilled value, which is the wrong shape whenever partial success has business meaning (fan-out fetches, batch writes, parallel migrations). `Promise.allSettled` returns a discriminated union per item, and the `'fulfilled' | 'rejected'` tag is what TypeScript narrows on. Note `reason: unknown` — the contract is sound: a rejected promise can reject with anything, including non-Errors. Resist `(reason as Error).message`; type-guard with `reason instanceof Error` instead. The cast `as Settled<T>[]` is the one acceptable spot — the lib type uses `any` for `reason`, and tightening it to `unknown` propagates the right discipline through the rest of the codebase.
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')]