TypeScript 7: an exhaustive switch over Sume job statuses

Turn a Sume job record into done, failed, canceled or running with a never check, so a new status breaks the build. Compiled with tsc 7.0.2 in strict mode.

4 min readSume
All posts

To handle every Sume job status safely in TypeScript, map the job record to a small discriminated union with a switch over status and assign the default branch to a never variable. The five documented statuses are queued, processing, completed, failed and canceled; if Sume adds a sixth and you widen your type, the compiler points at the one function that needs a new case. I compiled the 30-line version below with tsc 7.0.2, the current release on the npm registry on 2026-10-02, in strict mode.

This also fixes a common bug: treating anything that is not completed as failure. queued and processing are normal non-terminal states, and canceled is neither a success nor an error to retry.

Status to outcome

Status vocabulary from docs.sume.com Jobs and results, read 2026-10-02
StatusTerminalOutcome in the code
queuedNorunning
processingNorunning
completedYesdone, with artifact URLs
failedYesfailed, with code, message and retry flag
canceledYescanceled

The function

result and error are nullable on the job record, so the code reads them defensively: a completed job with no artifacts yields an empty list instead of throwing. The retryable flag comes from the job error metadata the errors page describes, and defaults to false when absent.

type Status = "queued" | "processing" | "completed" | "failed" | "canceled";
export type JobRecord = {
  id: string;
  status: Status;
  result: { artifacts: { id: string; url: string }[] } | null;
  error: { code: string; message: string; retryable?: boolean } | null;
};
export type Outcome =
  | { kind: "done"; urls: string[] }
  | { kind: "failed"; code: string; message: string; retry: boolean }
  | { kind: "canceled" }
  | { kind: "running"; status: "queued" | "processing" };

export function toOutcome(job: JobRecord): Outcome {
  switch (job.status) {
    case "completed":
      return { kind: "done", urls: (job.result?.artifacts ?? []).map((a) => a.url) };
    case "failed": {
      const e = job.error;
      return { kind: "failed", code: e?.code ?? "unknown", message: e?.message ?? "Job failed", retry: e?.retryable ?? false };
    }
    case "canceled": return { kind: "canceled" };
    case "queued":
    case "processing": return { kind: "running", status: job.status };
    default: {
      const unreachable: never = job.status; // a new status becomes a compile error
      throw new Error(`unhandled status ${unreachable}`);
    }
  }
}

Proof that the check works

Adding a sixth status to the Status type and recompiling gave Type '"paused"' is not assignable to type 'never' at the default branch. Running the compiled example through Node 22 with type stripping printed the artifact URL for a completed record and the error code for a failed one.

Keep your JobRecord type narrower than the real response. Describe only the fields you read, so an added field in the API never breaks your build, and only a changed status does.

Limits

The types here are hand-written from the documented job shape, not generated from the OpenAPI file; if you want generated types, @sume-com/sdk exports them for each operation, but check the version you install against the docs. Types do not validate runtime data. If a gateway or proxy hands you an HTML error page, job.status is undefined and the default branch throws, which is the behavior you want over silently treating it as running.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume