Solvedangular AOT Compiler requires public properties, while non-AOT allows private properties

I'm submitting a ... (check one with "x")

[X] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior
When writing a component, fields referred inside a template is allowed to be declared private and it will still work.

However, such private fields generate errors with the AOT compiler. They must be turned public.

Expected behavior
Be consistent. Either allow or not allow.

Reproduction of the problem

template: "<div>{{hello}}</div>"

class someModule {
    private hello: string;            <-- doesn't work with AOT compiler
}

Please tell us about your environment:
Windows 10.

  • Angular version: 2.0.0
  • Browser: all
  • Language: TypeScript
  • Node (for AoT issues): node --version = v4.3.1
44 Answers

✔️Accepted Answer

@aluanhaddad you have a big misunderstanding in here. There is no subset of Typescript in here. No one said that.

By using JiT (AKA the common way of doing Angular 2 since day 1), you transpile your code to ES5 to run it in the browser. Say you have:

@Component({
  selector: 'foo',
  template: '<div>{{name}}</div>'
})
export class FooComponent {
  private name = 'Hello';
}

That will generate, more or less something like:

define(["require", "exports"], function (require, exports) {
    "use strict";
    var FooComponent = (function () {
        function FooComponent() {
            this.name = 'Hello';
        }
        FooComponent = __decorate([
            Component({
                selector: 'foo',
                template: '<div>{{name}}</div>'
            })
        ], FooComponent);
        return FooComponent;
    }());
    exports.FooComponent = FooComponent;
});

Notice how FooComponent() contains a name property. It doesn't matter whether you put public or private at name because in ES5 that concept does not exist so you end with the same code.

When Angular runs that code, it will say. Ok, I have this html code, this template with a {{name}} and I can see a this.name in the ES5 code with a value, let's grab that and display it.

So far, so good.

So why do we have private and public if at the end the ES5 will not care? Well, the typescript compiler (and your editor) will complain if you try to access a private variable from where you can't do that:

class Foo {
  private bar = 10;
}

let f = new Foo();
console.log(f.bar);

Your editor will tell you that you cannot access bar and tsc (typescript compiler) will give you an error because you are trying to access a private variable.

Back in the component example, tsc doesn't complain because at compile time, no one tries to access that name field and the template will do the databinding at runtime where your code is not typescript anymore but ES5 and again, no visibility modifier in there.


That being said, with AoT, we sort of "convert" the template code to Typescript itself (again, Typescript, not any subset). Imagine the previous component, it will generate something like this:

createInternal(rootSelector:string):import3.AppElement {
  const parentRenderNode:any = this.renderer.createViewRoot(this.declarationAppElement.nativeElement);
  this._el_0 = this.renderer.createElement(parentRenderNode,'div',(null as any));
  this._text_1 = this.renderer.createText(this._el_0,'',(null as any));
  this._expr_0 = import7.UNINITIALIZED;
  this.init(([] as any[]),[
    this._el_0,
    this._text_1
  ]
  ,([] as any[]),([] as any[]));
  return (null as any);
}
detectChangesInternal(throwOnChange:boolean):void {
  this.detectContentChildrenChanges(throwOnChange);
  const currVal_0:any = import4.interpolate(1,'',this.context.name,'');
  if (import4.checkBinding(throwOnChange,this._expr_0,currVal_0)) {
    this.renderer.setText(this._text_1,currVal_0);
    this._expr_0 = currVal_0;
  }
  this.detectViewChildrenChanges(throwOnChange);
}

This is typescript code and it is our template converted to typescript code.

this._el_0 = this.renderer.createElement(parentRenderNode,'div',(null as any));
this._text_1 = this.renderer.createText(this._el_0,'',(null as any));

Here we create our div and a "placeholder" for a binding (AKA {{name}}).

A bit later we do:

const currVal_0:any = import4.interpolate(1,'',this.context.name,'');

We are accessing the name property from context where context is our FooComponent.

Then we do:

this.renderer.setText(this._text_1,currVal_0);

We get that placeholder from before and we assign to it the value of name AKA Hello.

Having this in mind, name has to be public, because if you set it private, this.context.name will raise an error since name is private and you cannot access it from outside the class.


So as TL;DR; there is no typescript subset. With JiT we convert all the code to ES5 and then in runtime we do the bindings. All the visibility modifiers are lost in that process, so it doesn't matter if you say public or private for that.

On the other hand, with AoT, we generate some typescript code for our templates, that will try to access those fields. If they are private, they simply cannot access those properties, hence, we have to put them as public.


@achimha All of this only applies to template <-> component interaction. A template is not part of the component class, so a template can only access to public properties from the component.

On the other hand, when we inject stuff using DI, we can use private because we only use that injected object within the component class and never in the template. That being said, if there is any use case where you need to inject something that needs to be used in the template, yes, you need public as well, but so far, that is not the case in any doc example.

Other Answers:

@robwormald Since a lot of official angular documentation uses private, isn't this a massive pit of failure for developers new to TypeScript? Considering that a component class is conceptually a cohesive unit, anyone not understanding that private properties are not private at runtime will have no intuition as to why their code no longer works under AOT.

While I am not of fan of the TypeScript's private keyword, because it is somewhat misleading, it sounds like JIT compiled components are working precisely because privacy is not enforced at runtime.

I would suggest removing the visibility modifiers on properties during AOT transformations and changing private to public for constructor parameter properties.

Edit: This issue should be re-opened

I agree this inconsistency is a nasty pitfall for developers. As @aluanhaddad says, a lot (most?) of the samples use private for DI in the constructor so developers get the habit especially since one is taught to never expose something that doesn't need exposing.

I believe this issue should be re-opened and addressed.

@robwormald Another, deeper problem that is emerging here, and in many other issues, is that AoT Angular code can not be written in TypeScript. It is written in a subset of TypeScript which is not, as far as I have seen, formally specified, documented, or acknowledged in any capacity.

This brings to mind a fair number of questions, including but not limited to the following:

  1. What is this language?
  2. When was this language introduced?
  3. Why was this language introduced?
  4. Who designed it?
    a. What are their credentials?
    b. What other languages have they worked on?
    c. Is it intended to be a general purpose language or a domain specific language?
  5. How does it improve on TypeScript as a language?
    a. What new abstraction mechanisms does it offer?
    b. What tasks does it simplify?
    c. What are its motivating usecases?
  6. Where is it formally specified?
  7. When did I adopt it?

This is the way it is because in JiT mode, we're not using Typescript - we're generating ES5 code, so there's no such concept as a "private" field, and we have no way of enforcing this.

Things that accessed from a template must be public, since they're being accessed outside of the class instance.

Related Issues:

608
angular Angular5.x lazyLoad problem, undefined is not a function
For others that find this issue via Google as i did: I had the same problem when trying to lazy load...
348
angular Cyclic dependency error with HttpInterceptor
I resolved simply not setting authService in constructor but getting in the intercept function. ...
277
angular Uncaught Error: Can't resolve all parameters for ...
You are missing an @Injectable() annotation on your ApiService Support requests like these should li...
266
angular Force reload/refresh current route with RouteReuseStrategy
Hi If you really need to trick the Router into reloading the component on each routerLink click ...
260
angular Misleading error message "Cannot find a differ supporting object '[object Object]'"
I just ran into the same issue I'm not sure if the recommended solution will work for my case ...
224
angular update 2 to 4 has problem [ts] Property 'map' does not exist on type 'Observable<Response>'.
I met the same problem with the angular cli 6.0.0 and rxjs 6.1.0 And I solved the problem by replaci...
170
angular Angular2 AoT Compiler Errors
pls try /cc @chuckjaz When I try to compile my project with ngc it throws the below error: Error: Er...
152
angular HttpClient.delete() cannot handle a body in its request
It would be great to have body param in .delete() We just migrated our project to HttpClient and for...
140
angular Http - Observable completed function not firing
Third callback haven't been called when error occures ES6 promises hasn't method finally only then a...
133
angular Using multiple components in different modules causing "Type X is part of the declarations of 2 modules" error
as @brandonroberts saids create a shared module like this: then use the SharedModule like this.. ...
112
angular Unsupported platform for fsevents@1.0.14: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
@DzmitryShylovich did you edit package.json only? if npm-shrinkwrap.json is still there please remov...
98
angular Angular v5 ngc compiler: Error encountered in metadata generated for exported symbol 'Subscription'
Got this problem I'm submitting a.. Current behavior Building an Angulary library using 5.0.0-beta.4...
98
angular 404 on route refresh in angular 4
Got it working Just adding .htaccess in root I'm submitting a.. Current behavior I created a new com...
97
angular routerLinkActive not updating when routerLink changed
I have a hack that seems to work After looking at the source code it looks like this.update() is als...
97
angular No provider for HttpClient!
If you are using angular v5 version import HttpClientModule in your app.module.ts after HttpModule T...
81
angular ɵDomAnimationEngine and ɵNoopAnimationEngine module missing in animations 4.2.1
@dubedoy I installed @angular/animations@4.1.3 and it worked again. I'm submitting a .. ...
80
angular Error: Runtime compiler is not loaded in angular6 --prod mode
Do not and i repeat do not import your feature modules in app module and also when addressing featur...
79
angular appending header in HttpHeaders from '@angular/common/http' doesn't work
@trotyl I didn't understand your comment I'm submitting a.. ...
77
angular Can't bind to 'formGroup' since it isn't a known property of 'form'
did you import ReactiveFormsModule? I'm submitting a .. ...
73
angular AOT Compiler requires public properties, while non-AOT allows private properties
@aluanhaddad you have a big misunderstanding in here There is no subset of Typescript in here No one...
73
angular [Bug] angular/elements: Failed to construct 'HTMLElement': Please use the 'new' operator
Hi I have solved this issue by changing the target:es5 in the tsconfig.json to target:es2015 these i...
70
angular Cannot run angular 2+ from file:/// - looks like 'base href="/"' is the issue
Thanks @Markovy @audrummer15 I got it working completely in a fairly complex angular 2 app with mult...
69
angular HttpClient fails to parse an empty 200 response in IE11
For my error I was able to fix the problem by setting the responseType: to 'text' in the options ...
66
angular Function calls are not supported in decorators when fullTemplateTypeCheck is not specified and @dynamic has no effect
Regarding Ward's repro: @wardbell The build will succeed / fail depending on the combination of angu...
62
angular error TS2451: Cannot redeclare block-scoped variable 'ngDevMode'
had to add this line in the main tsconfig I'm submitting a.. ...
62
angular Problem with ngFor
Wouldn't [(ngModel)]=testItems[i] do the trick? I think that the error is saying that you can assign...
61
angular Issue with importing Observable from rxjs/Rx (import-blacklisted)
You shouldn't import from 'rxjs' or 'rxjs/Rx' since either import will import the whole of rxjs whic...
53
angular [RC5]: Minified bundle breaks
@robertoforlani Hopefully someone will have time to write a comprehensive explanation soon In the me...
53
angular router-outlet is appending rather than replacing when using BrowserAnimationsModule
Trying this solved the problem for me: this.zone.run(() => { this.router.navigate(['/main']); }); Re...
52
angular Lazy loaded module in named outlet throws error
We have this Where proxy route component is simply [x] bug report [ ] feature request [ ] support re...
52
angular IVY Error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class
Not sure this will provide anyone relief or assist with figuring out what the root cause is but clea...
51
angular How to run angular 2 application on apache hosting server
Sorry to rock the boat I hope this doesn't attract more questions I'm only going to comment once :) ...
51
angular Support adding rel=canonical link tags using an included service
Eventually there will be some DocumentService part of Core that will handle both Meta/Link elements ...
50
angular Concept of Angular (ngZone + ChangeDetection) better than concept React, Vue (Virtual DOM)?
Concept of Angular (ngZone + ChangeDetection) better than concept React Vue (Virtual DOM)? If you ca...
50
angular I'd like to be able to use ngModel without specifying a name
Thank you all for the great feedback - very helpful! Here's how we are thinking about it: In the cas...
49
angular Model values not trimming automatically in angular 2
@laskoviymishka White space it already something If you are a programmer and think globally - yes ...
46
angular HttpClient - HttpErrorResponse not json but blob
I created this interceptor as a temporary solution until this one is fixed: I'm submitting a.. ...
45
angular Router's ActivatedRoute data returns empty {} if module is lazy
data is available only with this hell-like construction: And this is if you have children: ...
44
angular Misleading errormessage when using HostBinding with @animation trigger but no defined animations
The error message is not fine The error message says you're importing BrowserAnimationsModule incorr...
42
angular Memory leak when FormControlName created/destroyed few times
This issue has been around for nearly 3 years now (I usually don't like to start a message this way ...
41
angular Remove System.import() usage in favor of import()
I use a parser rule in my webpack configuration to disable the warning: https://webpack.js.org/confi...
41
angular Async event subscriber not updating UI after async call
Hi! The issue is that the async call result is outside ngZone thus not triggering the UI update You ...
40
angular Using router.navigate to navigate to another component does not invoke the onInit method
I have the same issue Angular is running in a Cordova app for iOS I tried the router-version 4.1.3 (...
37
angular Router: AoT compilation fails when using a function with loadChildren
Calling functions or calling new is not supported in metadata when using AoT This includes things li...
35
angular Provide a mock service using TestBed
I was having this issue as well however I noticed that my @component metadata still had the provider...
35
angular Angular2 download excel file from Web API, file is corrupt
@healkar01 I had the same issue and I resolved using native angular2 http request in this way: Backe...
35
angular 4.0.0-rc.6 [platform-server] - Cannot find module '@angular/animations/browser'. & other errors
(Just incase others find it) Make sure @angular/animations is installed as a dependency and the erro...
35
angular HttpClient mapping to typescript types not working
I agree with all the previous comments I find the syntax misleading widget.service.zip widget.servic...