Diagnostics
The TypeSpec compiler uses the diagnostic API to report errors and warnings in the specification.
Best practices
Section titled “Best practices”- ❌ Avoid using
throw
to report errors. Any exceptions thrown in this manner will be perceived as bugs in your library by the user. - ✅ Utilize the diagnostic API to report anticipated errors and warnings.
- ✅ Employ
reportDiagnostic
in a decorator,$onValidate
or$onEmit
- ❌ Refrain from using
reportDiagnostic
in an accessor (a function intended to be used in another library or emitter). Refer to the section on collecting diagnostics for more information.
- ✅ Employ
Diagnostic requirements
Section titled “Diagnostic requirements”- Each diagnostic MUST have a
code
. The complete code is the library name followed by the declared code. (<lib-name>/<local-code>
) - Each diagnostic MUST have a
severity
. It can beerror
orwarning
. Errors cannot be suppressed. - Each diagnostic MUST have at least one message. Using
default
as themessageId
will make it the default selection. - Each diagnostic message MAY have parameters to interpolate information into the message.
How to use
Section titled “How to use”Declare the diagnostics you plan to report
Section titled “Declare the diagnostics you plan to report”import { createTypeSpecLibrary } from "@typespec/compiler";
// in lib.jsexport const $lib = createTypeSpecLibrary({ name: "@typespec/my-lib", diagnostics: { // Basic diagnostic with a fixed message "no-array": { severity: "error", messages: { default: `Array is not allowed in my-lib models.`, }, },
// Parameterized message "duplicate-route": { severity: "error", messages: { default: paramMessage`Route '${"path"}' is being referenced in 2 different operations.`, }, },
// Multiple messages "duplicate-name": { severity: "warning", messages: { default: paramMessage`Duplicate type name: '${"value"}'.`, parameter: paramMessage`Duplicate parameter key: '${"value"}'.`, }, }, },} as const);
// Re-export the helper functions to be able to just call them directly.export const { reportDiagnostic, createDiagnostic };
This will represent three different diagnostics with the full names of:
@typespec/my-lib/no-array
@typespec/my-lib/duplicate-route
@typespec/my-lib/duplicate-name
Report diagnostics
Section titled “Report diagnostics”import { reportDiagnostic } from "./lib.js";
// Basic diagnostic with a fixed messagereportDiagnostic(program, { code: "no-array", target: diagnosticTarget,});
// Parameterized messagereportDiagnostic(program, { code: "duplicate-route", format: {path: "/foo"} target: diagnosticTarget,});
// Multiple messagesreportDiagnostic(program, { code: "duplicate-name", messageId: "parameter", format: {value: "$select"}, target: diagnosticTarget,});
Collect diagnostics
Section titled “Collect diagnostics”When attempting to report a diagnostic in an accessor, a good practice is not to report the diagnostic to the program directly, but return a tuple to let the user decide what to do. This prevents duplicate diagnostics emitter if the accessor is called multiple times.
import { createDiagnosticCollector, Diagnostic } from "@typespec/compiler";
function getRoutes(): [Route, readonly Diagnostic] { const diagnostics = createDiagnosticCollector(); diagnostics.add( createDiagnostic(program, { code: "no-array", target: diagnosticTarget, }), ); const result = diagnostic.pipe(getParameters()); // to pipe diagnostics returned by `getParameters` return diagnostics.wrap(routes);}
or manually
import { Diagnostic } from "@typespec/compiler";
function getRoutes(): [Route, readonly Diagnostic] { const diagnostics = []; diagnostics.push( createDiagnostic(program, { code: "no-array", target: diagnosticTarget, }), ); return [routes, diagnostics];}