angular: Improve error message: `TypeError: Cannot read property 'type' of null`

I’m submitting a…


[x] Feature request

Current behavior

If a custom library module, lets call it library-a, imports another custom library module, library-b, and the library-b module exports components/modules which are not also exported via typescript/javascript (i.e. library-b has @NgModule({exports: [...]}) but not export { ... }), then a very non-descriptive BUILD ERROR TypeError: Cannot read property 'type' of null is thrown with the following trace when performing ng build library-a.

BUILD ERROR
Cannot read property 'type' of null
TypeError: Cannot read property 'type' of null
    at /Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:15378:27
    at Array.forEach (<anonymous>)
    at removeSummaryDuplicates (/Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:15377:11)
    at TemplateParser.tryParseHtml (/Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:14806:34)
    at TemplateParser.tryParse (/Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:14799:21)
    at TemplateParser.parse (/Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:14780:27)
    at AotCompiler._parseTemplate (/Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:20483:43)
    at AotCompiler._createTypeCheckBlock (/Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:20250:23)
    at /Users/John/apps/tests/service-work/node_modules/@angular/compiler/bundles/compiler.umd.js:20220:27
    at Array.forEach (<anonymous>)

This made the problem very difficult to debug.

Expected behavior

Provide a better, more descriptive error message to aid in debugging.

Environment


Angular CLI: 6.0.5
Node: 8.9.1
OS: darwin x64
Angular: 6.0.3
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.6.5
@angular-devkit/build-angular      0.6.5
@angular-devkit/build-ng-packagr   0.6.5
@angular-devkit/build-optimizer    0.6.5
@angular-devkit/core               0.6.5
@angular-devkit/schematics         0.6.5
@angular/cdk                       6.1.0
@angular/cli                       6.0.5
@angular/material                  6.1.0
@angular/material-moment-adapter   6.1.0
@ngtools/json-schema               1.1.0
@ngtools/webpack                   6.0.5
@schematics/angular                0.6.5
@schematics/update                 0.6.5
ng-packagr                         3.0.0
rxjs                               6.2.0
typescript                         2.7.2
webpack                            4.8.3

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 28 (9 by maintainers)

Most upvoted comments

I had some time looking into the compiler to find out, what is actually going on here. Also posted some details over at ng-packagr/ng-packagr#1087, but I think my findings make better use in the angular repo here.

@Ventzy was on the right track already.

The problem is actually related to yarn/npm link.

  • Build Library A
  • yarn link Library A from dist folder, because Library B depends on it
  • Build Library B (which depends on A)

Internally the following happens:

  • While Library B is built, it refers to A (node_modules/library-a)
  • While identifing the internal dependencies of A, a recursive process searches for everything relevant
  • This process falls back into the real folder (…/dist/…) and not the symlinked folder
  • However: The Code which loads the Metadata for everything does NOT
  • => End result is, that there are Types (it’s failing on Pipes in my case) for which no meta data could be found and the removeDuplicates call fails, because there are null objects in the list of pipes …

Ultimately this is something the compiler team has to fix. Without further testing, I would expect that this only happens on Windows and the way symlinks work on this OS.

One important point:

  • This started happening for us after the upgrade from Angular 6 to 7
  • We have this project structure since Angular 2 and could build this sofar
  • So there probably was some change in how file paths are resolved in dependencies

For possible solutions / workarounds I could think of:

  • As @Ventzy mentioned already, do not use yarn/npm link and instead copy everything into the node_modules (i.e. build Library A, copy into node_modules)
  • No other ideas for now 😦

Thus - so far - and based on @pfeigl answer (thanks @pfeigl), what we do to automatically build a library (i.e. iri-loader), that is a dependency to another library, is to use the following alias in our package.json:

"iri-loader:build": "ng build iri-loader && npm i dist/iri-loader --save && rmdir node_modules\\@iri\\loader && xcopy /E /I dist\\iri-loader node_modules\\@iri\\loader"

PS. The issue holds true in macOS as well; below is the respective script to build the library there:

"iri-loader:build": "ng build iri-loader && npm i dist/iri-loader --save && rm node_modules/@iri/loader && cp -R dist/iri-loader node_modules/@iri/loader"

The issue seems to be with symlinked libraries in node_modules (I am on Windows). I am building library as usual into ./dist and then do npm link dist/library-b, which creates symlink node_modules/library-b -> dist/library-b. If library-b is installed that way, then ng build library-a fails with “Cannot read property ‘type’ of null” (only if components are listed in module exports). If I copy dist/library-b into node_modules, then ng build library-a is fine.

The reason behind npm link is to be able to build libraries with --watch and get changes propagated to the app, which won’t happen with copy. The only workaround that seems to be working so far is to list libraries in tsconfig.json paths like so

      "library-b": [
        "dist/library-b"
      ],
      "library-b/*": [
        "dist/library-b/*"
      ]

“ng g library” adds this automatically btw, but I had removed added paths each time. Up until now, “npm link” approach worked for libraries which does not export components used directly in templates of other libraries. I have libraries which export classes, services, and components which are dynamically instantiated and used by the app. It seems it chokes in above cross-library use case however.

Nice job debugging this, @pfeigl.

The good news is that Angular Ivy completely removes .metadata.json (and greatly simplifies path handling in the compiler in general), so this problem should be fixed completely with the Ivy compiler.

We’ve found the solution working for our project. Removing the components from the exports array in library-b’s module and keeping them exported only in public_api.ts.

Not sure whether it is relevant but I don’t use yarn/npm link and I still get this error. I only get it when I use

skipTemplateCodegen: false

If I skip the template code generation it builds fine. My library also doesn’t depend on another library in my monorepo, only on 3rd party libs (primeng, bootstrap, fontawesome etc)

@dherges easy. Let’s consider a setup as in original post above with library-a and library-b modules.

@NgModule({
  imports: [
    TranslateModule.forChild(),
    CommonModule,
    ReactiveFormsModule,
    TranslateModule,
    FlexLayoutModule,
    MatButtonModule,
    MatCheckboxModule,
    MatProgressSpinnerModule,
  ],
  declarations: [
    CustomButtonComponent,
    CustomHeaderButtonComponent,
    LoadingOverlayComponent,
    NoRowsOverlayComponent
  ],
  exports: [
    CommonModule,
    ReactiveFormsModule,
    TranslateModule,
    FlexLayoutModule,
   CustomButtonComponent, // <--
    CustomHeaderButtonComponent, // <--
    LoadingOverlayComponent, // <--
    NoRowsOverlayComponent // <--
  ],
  entryComponents: [
    LoadingOverlayComponent
  ],
  providers: [
    ConfigService,
    { provide: APP_INITIALIZER, multi: true, useFactory: configServiceInitializerFactory, deps: [ConfigService] }
  ],
})
export class LibraryBModule {}

if you import this module into library-a module you get the error while building library-a. But if you don’t export the components (marked with <--) from the module the build will work. And of course to be able to import them elsewhere you need to export them from the library public_api.ts barrel:

export * from './lib/components/ag-grid/cell/custom-button.component';
export * from './lib/components/ag-grid/header/custom-header-button.component';
export * from './lib/components/ag-grid/loading-overlay.component';
export * from './lib/components/ag-grid/no-rows-overlay.component';