Skip to content

Declarative diagnostics

defineDiagnostic pins the shape of a diagnostic — code, message, help, url — once, so each throw site only carries the parts that actually vary (source, snippets, args). The runtime parallel of Rust miette's #[derive(Diagnostic)].

Edit the definition, args, source, or snippets below and watch the rendered output update live. The right pane shows the equivalent TypeScript so you can paste it straight into your codebase.

definition
args (JSON)
per-throw input
snippets
rendered output
Error: TS2345

   × Argument of type 'string' is not assignable to 'number'.

   ╭───[app.js:6826:21]
 0 │ function divide(a: number, b: number) { return a / b; }
 1 │ 
 2 │ divide(10, "two");
   ·           ──┬──   
   ·             ╰── string passed here     
   ╰───

‽ Pass a number, or change the parameter type.

equivalent code
const TS2345 = defineDiagnostic<{ actual: string; expected: string }>({
  code: "TS2345",
  message: ({ actual, expected }) => `Argument of type '${actual}' is not assignable to '${expected}'.`,
  help: ({ actual, expected }) => `Pass a ${expected}, or change the parameter type.`,
});

throw new TS2345({
  source,
  snippets: [
    {
      "span": [
        67,
        72
      ],
      "label": "string passed here"
    }
  ],
  args: {
    "actual": "string",
    "expected": "number"
  },
});

Use {placeholder} in templates

The templates here use {name} placeholders for the playground. In real code you'd write a normal arrow function:

ts
message: ({ actual, expected }) =>
  `Argument of type '${actual}' is not assignable to '${expected}'.`,

Same result, full TypeScript type-checking on the args shape.

For the API contract, see defineDiagnostic. For how it fits into the rest of the library, see Reusable diagnostics.

Released under the Apache 2.0 License.