Skip to content

Access to TypeScript private members in templates consequences #70514

Description

@cyrilletuzi

Which @angular/* package(s) are the source of the bug?

compiler

Is this a regression?

No

Description

This issue is about #70188, landed in v22.2.0-next.3, which now allows access to TypeScript private members in templates.

I understand the technical motivation behind it, and I am not against it in principle, but behind what seems like a nice and minor change, there are in fact a lot and major consequences, and I want to be sure these consequences have been evaluated before it becomes stable, and, as an author of tools which would be impacted, to have an official response that this is indeed the new way to go.

Since the beginning of Angular, the fact that the templates did not have access to TypeScript private members led to a very common pattern to respect the principle of separation of responsibilities: put in a simplified way, public members for what is accessible in the templates (view / data display), private members for what must be accessible only in the class and not in the template (data management). Some common examples:

@Component()
export class SomeComponent {
  // dependency injection
  private readonly someService = inject(SomeService);

  // give templates only access to the readable part (same for resources and signals forms)
  private readonly someWritableSignal = signal('');
  readonly someSignal = this.someWritableSignal.asReadonly();
}

Now that TypeScript private members are also accessible in the templates, it means the only way still available to achieve this separation and to not allow to access and to do anything in templates is to use native private members:

@Component()
export class SomeComponent {
  // dependency injection
  readonly #someService = inject(SomeService);

  // give templates only access to the readable part (same for resources and signals forms)
  readonly #someSignal = signal('');
  readonly someSignal = this.#someSignal.asReadonly();
}

This has major consequences:

  1. while it will not break existing applications at the moment of the Angular 22.2 update, suddenly having access in templates to everything already existing in private members for the code added afterwards is a major change, and wanting to keep private stuff not accessible from templates would require massive refactors
  2. it introduces new potential reactivity issues (see comment below)
  3. confusion, as it will imply a mix usage of TypeScript and native syntaxes depending on the scenario, and the differences between TypeScript and native private members are subtle and not mastered by most developers, some of them not even being aware of the native syntax
  4. a major change of habit (the official documentation itself uses private everywhere, not #)
  5. some complications in tests: while private someMember is still accessible via ['someMember'], #someMember is not accessible at all from outside the class instance; while it is ok for dependency injection, where Angular providers are the way to go, it can be more difficult for other cases where more classic stubs / mocks / fakes are required

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw


Please provide the environment you discovered this bug in (run ng version)

Angular 22.2.0-next.3

Anything else?

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: compilerIssues related to `ngc`, Angular's template compilergemini-triagedLabel noting that an issue has been triaged by gemini

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions