question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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

See original GitHub issue

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

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:30
  • Comments:51 (19 by maintainers)

github_iconTop GitHub Comments

76reactions
Foxandxsscommented, Oct 24, 2016

@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.

57reactions
aluanhaddadcommented, Oct 5, 2016

@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

Read more comments on GitHub >

github_iconTop Results From Across the Web

Angular 6 private methods - Stack Overflow
Why AOT Compiler requires public properties, while non-AOT allows private properties? With JIT we convert all the code to ES5 and then at ......
Read more >
Angular and TypeScript Private Properties. | by Vikash Singh
Why AOT Compiler requires public properties, while non-AOT allows private properties? With JIT we convert all the code to ES5 and then at ......
Read more >
Ahead-of-time (AOT) compilation - Angular
The compiler can only reference exported symbols. Decorated component class members must be public. You cannot make an @Input() property private or protected....
Read more >
Angular Aot Build Failing Because Of Private Member - ADocLib
Solvedangular AOT Compiler requires public properties while nonAOT allows private [X] bug report search github for a similar issue or PR before.
Read more >
Ahead-of-Time Compilation in Angular - Minko Gechev
When the compilation takes place? ... This allows the JavaScript virtual machine to perform property access caching and execute the change ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found