Diagnostics
The TypeSpec compiler uses the diagnostic API to report errors and warnings in the specification.
Best practices
Section titled âBest practicesâ- â Avoid using
throwto 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
reportDiagnosticin a decorator,$onValidateor$onEmit - â Refrain from using
reportDiagnosticin 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 beerrororwarning. Errors cannot be suppressed. - Each diagnostic MUST have at least one message. Using
defaultas themessageIdwill 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
These diagnostics (and the libraryâs linter rules) can also be referenced by a short name where the package scope is stripped â for example my-lib/no-array â when suppressing or configuring them. The full name is always accepted.
Custom alias
Section titled âCustom aliasâBy default the short name is the package name with the scope stripped (@typespec/my-lib â my-lib, @<scope>/typespec-<name> â <name>). A library can provide a custom alias used instead:
export const $lib = createTypeSpecLibrary({ name: "@azure-tools/typespec-client-generator-core", alias: "tcgc", diagnostics: { /* ... */ },} as const);With the alias above, diagnostics and linter rules can be referenced as tcgc/<code> (e.g. #suppress "tcgc/no-foo").
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];}