For years, one of the biggest pain in Angular was to debug an error. In the error message there was no clear stack trace indicating where the actual error was. Ivy improved it a lot because of the locality principle and gives us the line number too with the error message. In Angular 10.1, another error logging improvement by the AOT compiler is introduced. Let's see how the error logging has improved over the years and what this new change brings us.

Before Ivy#

Let's create a application in Angular 8 then migrate it slowly to Angular 9 and 10. This will allow us to see the stack trace available to see how the compiler's error logging improved.

You can create a new Angular 8 application by using below command

npx @angular/cli@8.3.28 new errordemo

Let's take an example where we try to assign a variable to template property in component.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `${title}`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'errordemo';
}

If you see above code we are trying to bind an variable to template which should be string. But the error which we receive is below

ERROR in src/app/app.component.ts(5,16): error TS2304: Cannot find name 'title'. 

Which is confusing as the error says it is not able to find the variable name title, which exists. But the actual error should be something like template property must be string.

Let's look at few more errors like trying to bind a variable which does not exist.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `{{name}}`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'errordemo';
}

Surprisingly, there is no error, until we try to create a production build.

Updating to Angular 9 with Ivy and AOT compilation#

Let's update the application to Angular 9 and see what we get.

The same template now gives theis meaningful error.

ERROR in src/app/app.component.ts:5:16 - error TS2339: Property 'name' does not exist on type 'AppComponent'.

5   template: `{{name}}`,

Now you can easily find the line number and what the actual error is.

But what about the below template.

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `${name}`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'errordemo';
}
ERROR in src/app/app.component.ts:5:13 - error NG1010: template must be a string        
    
    5   template: `${name}`,
                  ~~~~~~~~~
    src/app/app.module.ts:9:5 - error NG6001: The class 'AppComponent' is listed in the declarations of the NgModule 'AppModule', but is not a directive, a component, or a pipe. Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.       

    9     AppComponent
          ~~~~~~~~~~~~

      src/app/app.component.ts:9:14
        9 export class AppComponent {
                       ~~~~~~~~~~~~
        'AppComponent' is declared here.

Now we have more meaningful error with line number and message. But still there is some information which is not useful and can confuse the developer, like the below error:

The class 'AppComponent' is listed in the declarations of the NgModule 'AppModule', but is not a directive, a component, or a pipe. Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.

If you check the code, there is nothing wrong with the component. The problem is that the template should be a string with a property name enclosed in curly braces rather than a template expression.

When this component is sent to the compiler, this component compilation already failed and the compiler is not taking it as a valid component. It then throws the next line of error.

What if the compiler could understand that there are cases where the values should be known at compile time. It would be great if it could throw a more meaningful error.

Angular 10 to the rescue#

Angular 10 is coming up with a feature for compiler, where it can throw more meaningful error. With a new feature about to land in Angular 10.1 which will give us more correct information with a valid reason to why the compilation failed.

Currently with Angular 10.0, we get an error like this:

ERROR in src/app/app.component.ts:5:16 - error TS2552: Cannot find name 'text'. Did you mean 'Text'?

5   template: `${text}`,
                 ~~~~

  node_modules/typescript/lib/lib.dom.d.ts:15438:13
    15438 declare var Text: {
                      ~~~~
    'Text' is declared here.

Which is still not as correct or useful as we'd like. In the upcoming release, we will get the below error which is easier to understand.

It gives us more explanation that the template should be a string and the value should be available before it can be compiled. In this case, the text variable holds an HTMLElement.

You can read more about this feature in this Pull Request

Conclusion#

With the initial versions of Angular, it was a real pain to understand the errors thrown by the compiler. Fortunately, the compiler error message have improved over the years.

Ivy started giving the errors with useful information like line numbers. With this feature it will be more easy to identify the issues where the values cannot be determined dynamically and should be known to the compiler in advance.

If you want to understand more on how compilers work, you should refer to the below articles by Ajit Singh

  1. Under the hood of the Angular Compatibility Compiler (ngcc)
  2. An in-depth overview of Angular compilers