In Angular v13.2.0, we released extended diagnostics, a new feature in the Angular compiler which gives more insight into your templates and how you might be able to improve them. These diagnostics give compile-time warnings with precise, actionable suggestions for your templates, catching bugs before you notice them.
A Common Mistake
Take the following example, which uses a two-way binding to display the message “My favorite fruit is: banana”.
You might be surprised to learn that this does not work. “Banana” gets dropped and does not display.
Screenshot of the running application which reads “My favorite fruit is: “. The fruit is missing and not displayed.
What happened? The code looks perfectly reasonable. However, building this with v13.2.0 now shows a compiler warning:
This warning calls out the exact mistake: the two-way binding syntax was backwards. As is, this is actually an event binding which happens to be named [fruit]. That’s technically valid and events can be named with that scheme; however it is almost certainly not what was wanted here. Extended diagnostics are intended to identify patterns like this, which are actually valid in a way that is almost certainly not intended by the developer and likely to lead to bugs.
This warning also suggests a fix. For two-way bindings, the banana (parenthesis) is supposed to go inside the box (square bracket), and writing this as [(fruit)]=”favorite”works as originally intended. Getting this syntax backwards is a common mistake, and making it in a more complex application could easily cost a few hours of debugging. This extended diagnostic catches the mistake before losing valuable time to debugging.
Angular v13.2.0 also includes one other warning which the compiler emits when it encounters a nullish coalescing operator (??) with a non-nullable input. The operator is not needed in this case since the right side will never be seen by the user. This is probably not intended by the developer and is a great opportunity for code cleanup. The compiler leverages its understanding of the programs types to recognize the input as non-nullable (type does not include null or undefined).
Demo of extended diagnostics showing invalid banana in box and nullish coalescing not nullable warnings as well as their fixes.
Check out the docs for more information about these diagnostics.
Extended diagnostics are shown in both the compiler via ng build and ng serve as well as the Angular Language Service. They are emitted as warnings by default so as not to interrupt a focused edit/refresh loop when they do not need to. It can be quite annoying to have your build broken because a ?? did not like that you removed a null and have not yet fixed it. Extended diagnostics are intended to identify developer mistakes early without breaking this flow, and warnings are a useful middle ground.
That said, developers may want to customize extended diagnostics, so Angular supports configuration via the tsconfig.json file. You can choose the category (warning, error, or suppress) for each specific diagnostic and a default category for all other diagnostics. In this case, we are suppressing the nullish coalescing check to ignore it altogether, while making all other diagnostics (invalid banana in box) a hard error that blocks the build because that mistake is quite common.
See our documentation for more details on configuring diagnostics and the complete list of check names.
One common pattern is to treat certain diagnostics as warnings locally to keep developer flow while treating them as errors in your continuous integration (CI) system. It is okay to ignore an unneeded ?? operator while in the middle of debugging another problem, but it is better for the project to fix it before merging your code.
The Angular CLI makes this pattern easy to set up. It supports multiple configurations for a single builder, which is typically how users manage development vs production builds. We can add a new configuration for CI builds with stricter type checking. All it takes is updating the angular.json file:
This means that ng build -c ci will use tsconfig.ci.json for type checking. All we need to do is make this file with stricter diagnostics settings:
Now ng build -c ci will fail with hard errors if any extended diagnostics are emitted, but ng serve and ng build -c development will treat them as warnings to avoid impeding your local development workflow. Update your CI system to use ng build -c ci and you can be confident that all merged PRs follow these best practices.
While this is a powerful tool, keep in mind that the Angular team may add new extended diagnostics in minor releases, and defaultCategory will apply to them. Treating those diagnostics as errors could introduce an error in a minor version update, so think carefully before using defaultCategory: “error”. See the docs for more information.
How it Works
We are starting with two checks, but hoping to add many more in the future. The extended diagnostics system is designed to be easily extensible and make future checks straightforward to author. Diagnostics use a consistent interface for checking a given Angular template which looks like (code snippets simplified and annotated for clarity):
Take a look at this source code for the invalid banana in box diagnostic:
Because the system is designed to provide a frictionless way to add new diagnostics, the core logic is simplified to “Given a node in a template’s abstract syntax tree, return a diagnostic for it, if appropriate”. Most of the work is handled by TemplateCheckWithVisitor, which is a utility that sets up the visitor pattern necessary to visit and check every node in the tree. We can even ask the type system about the template to better understand template expressions, which is how we identify nullish coalescing operators with non-nullable inputs.
The compiler itself keeps a list of all extended diagnostics, manages their configuration, executes them, and collects all the results. This is a part of the type-checking phase of the compiler, see this breakdown for a broader picture of how the compiler works.
The team cannot wait to expand the system with more checks to give extra safety rails for developers. Extended diagnostics can be a very useful tool for detecting anti-patterns or mistakes in Angular templates before developers spend a lot of time debugging them. As a result, we are focusing this system around diagnostics which identify correctness issues, performance regressions, or maintainability challenges.
We are still early in this process, but some initial ideas include:
Require === and !== in templates (as opposed to == and !=).Verify internationalization best practices (all text has i18n attribute, every message has a description, etc.).Verify accessibility best practices (all images have an alt attribute, all routes have titles, etc.).Detect inline <style /> tags in Angular templates since these break CSS inlining and view encapsulation.Detect relative anchor tags without a routerLink attribute to prevent unnecessary page refreshes.Detect negated async pipes to reduce layout thrashing.Detect non-nullable optional chaining ?. operator.Detect accidental nullish coalescing of pipe arguments.
If any of these sound interesting, consider upvoting and following the issues. We use this feedback to identify the most valuable features for the community and prioritize what we work on next.
Some of these diagnostics might sounds like they belong in a linter, formatter, or other static analysis tool, but we think of extended diagnostics as a distinct system. Linters typically identify a wide variety of potential issues in a codebase, with equally varying severity and confidence. Low confidence (unsafe API) or low severity (style) checks are important for code health and quality, but often do not need to interrupt a developer’s workflow. These checks are typically set up to run as part of CI rather than in local development. By contrast, extended diagnostics is intended to run as part of the developer’s edit/refresh loop, providing immediate feedback about potential issues in their code which could prevent extensive debugging.
Some checks are well-suited as an extended diagnostic, while others are better implemented as lint checks or via some other static analysis tools. Exactly where a particular check belongs will depend on the mistake it is trying to catch, how confident it can be, how actionable the issue is, and how important it is to a developer actively writing code.
If you have an idea for an extended diagnostic that is not listed here, we would love to hear it! Here are some guidelines for an ideal diagnostic:
Detect a common, non-obvious developer mistake with Angular templates.Clearly articulate why this pattern can lead to bugs or unintended behavior.Suggest one or more clear solutions.Have a low (preferably zero) false-positive rate.Apply to the vast majority of Angular applications (not specific to an unofficial library).Improve program correctness, performance, or maintainability (not style, that responsibility falls to a linter).
If you think your idea is a good candidate for an extended diagnostic, please consider filing a feature request.
Beyond new diagnostics, we are also hoping to expand the system’s capabilities as a whole. This includes generating “quick fixes” which are suggestions from the Angular Language Service which can automatically fix the error and correct your code with a single click. Longer term we are also hoping to support third-party diagnostics authored by the community. Ideally, you could ng add my-super-cool-library and automatically get extended diagnostics that catch common mistakes for that library. We are still thinking through how to do this in a secure and maintainable fashion, so keep an eye out for future announcements.
Finally, a huge shout out to our summer intern Daniel Treviño, who contributed most of the design and implementation for extended diagnostics. Awesome job!