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.
snippets
- →
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.
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.