8000 feat(compiler): Support instanceof operator in Built-in control flow (Angular templates) · Issue #59975 · angular/angular · GitHub
[go: up one dir, main page]

Skip to content

feat(compiler): Support instanceof operator in Built-in control flow (Angular templates) #59975

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sunio00000 opened this issue Feb 17, 2025 · 12 comments
Labels
area: compiler Issues related to `ngc`, Angular's template compiler
Milestone

Comments

@sunio00000
Copy link
sunio00000 commented Feb 17, 2025

Which @angular/* package(s) are relevant/related to the feature request?

compiler

Description

I'm interested in hearing opinions on allowing the use of the instanceof operator within the @if control block. Sometimes, I want to expose a template based on the instance of an object rather than its properties.

While Angular templates is designed with a declarative UI composition in mind, I wonder if instanceof might not be considered declarative enough. If there is any reason for not considering it, could it be related to performance or debugging concerns?

Example:

@if(myObject instanceof SomeClass) {
    <!-- This template will only be displayed if myObject is an instance of SomeClass -->
    <div class='some'>
         Content for SomeClass instance.
    </div>
}

Proposed solution

Here is a stackblitz

Alternatives considered

Pre-Computed Properties: Compute the instanceof result in the component and bind it to the template.
Custom Pipe: Use a custom pipe to encapsulate the instanceof logic.
Custom Structural Directive: Create a directive to handle the instanceof check declaratively.

@JeanMeche JeanMeche added the area: compiler Issues related to `ngc`, Angular's template compiler label Feb 17, 2025
@ngbot ngbot bot added this to the needsTriage milestone Feb 17, 2025
@JeanMeche
Copy link
Member
JeanMeche commented Feb 17, 2025

While this is totally doable (in a similar way we implemented support for typeof recently (#59357)), the problem with instanceof is that is requires the symbol to be exposed in the template (the same way as enums for example).

This is the primary reason we haven't implemented it yet.

Also this is a variant of #43485.

@sunio00000
Copy link
Author
sunio00000 commented Feb 17, 2025

@JeanMeche
Thank you for your quick response.
I understand that symbol.hasInstance needs to be exposed in order to use instanceof. As you mentioned, can I understand that the reason it hasn't been implemented yet is because there are many considerations regarding exposing the symbol?
SO Will this implementation be possible soon? Or is this something you are considering?

@JeanMeche
Copy link
Member
JeanMeche commented Feb 17, 2025

It's the SomeClass symbol that needs to be exposed to the template via a class property, eg

class MyComponent {
  protected SomeClass = SomeClass; 
}

This is due to template only having access to the class properties and not the lexical scope.
This makes the usage of instanceof unpracticle in templates.

@sunio00000
Copy link
Author
sunio00000 commented Feb 17, 2025

Explicitly exposing the class symbol as a class property to use it in the template is quite awful to read. I now understand the implementation challenges—since the template itself has no way of recognizing the class symbol.

Would it be a risky approach to create a custom pipe in the form of a type guard function?

For example, something like this:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'isInstanceOf'
})
export class IsInstanceOfPipe implements PipeTransform {
  transform(value: any, classType: any): boolean {
    return value instanceof classType;
  }
}

Usage in the template:

@if(myObject | isInstanceOf: SomeClass) {
   <div>
      This is an instance of SomeClass!
   </div>
}

Would this be a feasible alternative, or are there significant risks involved?

@JeanMeche
Copy link
Member

The custom pipe would have the same issue, you would need to expose the class symbol.

Also I'm not sure that with a pipe, type narrowing would work.

@sunio00000
Copy link
Author

Ooooops. You're right! Thanks for your answer! 😊

@jnizet
Copy link
Contributor
jnizet commented Feb 17, 2025

I would add that, AFAIK, most Angular developers prefer using plain interfaces over classes for data, and that a discriminated union type is often a good alternative to a class hierarchy and instanceof:

interface RegularUser {
  type: 'regular';
  name: string;
}

interface AdminUser {
  type: 'admin';
  name: string;
  permissions: Array<string>;
}

type User = RegularUser | AdminUser;
@if (user.type === 'admin') {
  {{ user.permissions | json }}
}

@sunio00000
Copy link
Author

@jnizet thx for answer!
I agree with your opinion that most Angular developers use plain interface-based data in templates. I also follow this approach in most cases.

The suggestion I mentioned above arises from the specific requirements of the project I am developing, which necessitate some level of abstraction. I wanted to avoid expanding by creating separate properties, and I hope you understand this intent.

I acknowledge that using instanceof in templates is highly restrictive and applicable only to very specific situations. I am also reconsidering this approach myself. However, if the argument is that "instanceof is unnecessary because most Angular developers differentiate data using plain interfaces," I believe that reasoning is somewhat unreasonable.

I think Angular templates should be able to implement TypeScript or JavaScript expressions as they are, as stated in the "title" of #43485

@aceArt-GmbH
Copy link

It's the SomeClass symbol that needs to be exposed to the template via a class property, eg

class MyComponent {
protected SomeClass = SomeClass;
}

This is due to template only having access to the class properties and not the lexical scope. This makes the usage of instanceof unpracticle in templates.

Wouldn't it be possible to make the template part of the class? (and not just a separat extended class)
This is the way we use and think about components anyway (as one combined thing).

And this would also allow using private properties in the template.

@JeanMeche
Copy link
Member

This wouldn't fix the issue with accessing symbols that are not class members.

@aceArt-GmbH
Copy link

This wouldn't fix the issue with accessing symbols that are not class members.

sure, but you could just import SomeClass without requiring protected SomeClass = SomeClass;, right?

@aceArt-GmbH
Copy link

This is the primary reason we haven't implemented it yet.

IMO, it is still better to write SomeClass = SomeClass; and @if(myObject instanceOf SomeClass) { instead of

isSomeClass(candidate: unknown): candidate is SomeClass {
  return candidate instanceof SomeClass;
}

with @if(isSomeClass(myObject)) {.

Shorter code, clearer intent, more like TS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: compiler Issues related to `ngc`, Angular's template compiler
Projects
None yet
Development

No branches or pull requests

4 participants
0