Skip to content

Type narrowing for NgSwitch and NgSwitchCase #20780

Closed

Description

@chuckjaz

Current behavior

No type narrowing is performed when using NgSwitch and NgSwitchCase.

Expected behavior

Narrow the types used in the NgSwitchCase based on the selection criteria.

See #17953 (comment) and following for more details.

Activity

  1. added
    area: coreIssues related to the framework runtime
    featureLabel used to distinguish feature request from other issues
    on Dec 4, 2017
  2. added this to the Backlog milestone on Jan 23, 2018
  3. dirkluijk commented on Feb 1, 2018

    @dirkluijk
    Contributor

    Looking forward to see type guard support in language-service as well!

    My simple use case:

    interface Animal {
      type: 'fish' | 'bird';
    }
    
    interface Fish extends Animal {
      type: 'fish';
      foo: string;
    }
    
    interface Bird extends Animal {
      type: 'bird';
      bar: string;
    }
    
    @Component({})
    class MyComponent {
      animal: Animal;
    
      isFish(animal: Animal): animal is Fish {
        return animal.type === 'fish';
      }
    
      isBird(animal: Animal): animal is Bird {
        return animal.type === 'bird';
      }
    }

    Use case 1

    <app-fish *ngIf="isFish(animal)" [foo]="animal.foo"></app-fish>
    <app-bird *ngIf="isBird(animal)" [bar]="animal.bar"></app-bird>

    Use case 2

    <ng-container [ngSwitch]="true">
      <app-fish *ngSwitchCase="isFish(animal)"></app-fish>
      <app-bird *ngSwitchCase="isBird(animal)"></app-bird>
    </ng-container>

    Are both use cases covered?

  4. waterplea commented on Sep 9, 2019

    @waterplea
    Contributor

    This would be a great addition, having an issue similar to case 2 that @dirkluijk posted.

  5. amitport commented on Oct 31, 2019

    @amitport
    Contributor

    @chuckjaz any updates? Anyone working on this? Thanks

  6. dirkluijk commented on Jan 21, 2020

    @dirkluijk
    Contributor

    Will this be solved in Ivy?

  7. SchnWalter commented on Jan 21, 2020

    @SchnWalter
    Contributor

    No, this isn't solved by Ivy, if anything, Ivy will cause more people to realize how badly they need ngSwitch to support type narrowing.

  8. added
    area: commonIssues related to APIs in the @angular/common package
    area: compilerIssues related to `ngc`, Angular's template compiler
    and removed
    area: coreIssues related to the framework runtime
    on Mar 17, 2020
  9. klemenoslaj commented on Jun 24, 2020

    @klemenoslaj
    Contributor

    This would really clean up a lot of template code. How come this is such a low priority?

  10. Lonli-Lokli commented on Jan 21, 2021

    @Lonli-Lokli

    It has 63 upvotes so seems like pretty needed feature.

  11. added
    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
    on Jan 21, 2021
  12. 13 remaining items

  13. flevi29 commented on Jun 16, 2023

    @flevi29

    5 years later, still not implemented :(

    Using functions as @dirkluijk commented is not an option for me as this functions will run each time the change detection runs and their pure purpose is to prevent type checking errors on build.

    I've been wondering if this is true, switch would have to re-run each time too, it needs to check which template to render. So a nested "if-else-if" ugly depressing monstrosity should be just as good. Of course there's some added overhead but it should be absolutely minimal. The biggest headache is the resulting monster lasagna code. It's so much more verbose and hard to read than it has to be.

  14. Jrubzjeknf commented on Jun 16, 2023

    @Jrubzjeknf

    Check discussion #50719, header switch block .

    #switch has several major benefits over NgSwitch:

    It does not require a container element to hold the condition expression or each conditional template.
    It can support template type-checking, including type narrowing within each branch.

  15. stealthAngel commented on Aug 25, 2023

    @stealthAngel

    Although I like the answer of @dirkluijk There is a much simpler solution:

    export type ShapeType = 'circle' | 'square' | 'rectangle';
    
    export interface Shape {
      type: ShapeType;
    }
    
    export interface Circle extends Shape {
      radius: number;
    }
    
    export interface Square extends Shape {
      sideLength: number;
    }
    
    export interface Rectangle extends Shape {
      width: number;
      height: number;
    }
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
    })
    export class AppComponent {
      json: string = `[{
        "type": "circle",
        "radius": 11
      },
      {
        "type": "square",
        "sideLength": 15
      },
      {
        "type": "rectangle",
        "height": 10,
        "width": 20
      }
      ]`;
    
      shapes: Shape[] = JSON.parse(json);
    }
    
    <ng-container *ngFor="let shape of shapes" [ngSwitch]="shape.type">
      <app-circle *ngSwitchCase="'circle'" [shape]="shape"></app-circle>
      <app-rectangle *ngSwitchCase="'rectangle'" [shape]="shape"></app-rectangle>
      <app-square *ngSwitchCase="'square'" [shape]="shape"></app-square>
      <div *ngSwitchDefault>Onbekend</div>
    </ng-container>
    
    @Component({
      selector: 'app-square',
      templateUrl: './square.component.html',
      styleUrls: ['./square.component.css']
    })
    export class SquareComponent {
      @Input()
      public shape: Shape | undefined;
    
      protected get square(): Square {
        return this.shape as Square;
      }
    
      constructor() { }
    }
    

    In the component i'm casting it to the shape.

    see my git repository for an example:

  16. baratgabor commented on Aug 30, 2023

    @baratgabor

    One technique that can help:

      // In component.ts
      // This type forces to create an object that always has exactly the same members & values as the original string union.
      animalType: { [K in Animal['type']]: K } = {
        bird: 'bird',
        fish: 'fish'
      }
    <ng-container [ngSwitch]="animal.type">
      <app-fish *ngSwitchCase="animalType.bird"></app-fish>
      <app-bird *ngSwitchCase="animalType.fish"></app-bird>
    </ng-container>
  17. maxime1992 commented on Aug 31, 2023

    @maxime1992

    @baratgabor this is about NgSwitch and NgSwitchCase. Nothing will be enforced on the template side as far as I know with your code above

  18. jpike88 commented on Oct 20, 2023

    @jpike88

    I've banned use of ngSwitch in our projects because not only does it not have type narrowing, there is zero type awareness in the case settings meaning any old thing can be used in the cases and we don't know about the problem until a bug happens.

  19. juane1000 commented on Oct 20, 2023

    @juane1000

    I think this may be locked going forward because the new template flow control syntax will enable type narrowing. NgSwitch and NgIf will be sunset according to previous angular team comments on their stream. Existing codebases will have to migrate if they want the type safety in the templates.

  20. jpike88 commented on Oct 20, 2023

    @jpike88

    Does anyone know how long roughly that RFC is going to take to be implemented? It still seems to be in early stages...

  21. JeanMeche commented on Oct 20, 2023

    @JeanMeche
    Member

    The new control flow is pretty much implemented an available in the current RC.
    It will be available as a developer preview in v17 (release in early november).

  22. jpike88 commented on Oct 20, 2023

    @jpike88

    Thanks. I'll note that I'm just trying it out now on the current RC (I added the appropriate stuff to angularCompilerOptions), and neither vscode (typescript and angular extension on latest) or ng build is flagging any type related issues. In that screenshot, isDebug variable is a boolean... just an FYI

    Screenshot 2023-10-20 at 17 02 03
  23. jpike88 commented on Oct 23, 2023

    @jpike88

    Sorry that wasn't very clear. What I meant is that I expected there to be some sort or type narrowing to be happening in those case statements as they don't match the type of the isDebug variable

    edit: looks like it's appropriate for me to open issues about this. ill start doing that

  24. alxhub commented on Dec 15, 2023

    @alxhub
    Member

    Closing as we have no plans to support coordinated type-checking of multiple structural directives, but @switch does support type-checking (it's about time!)

  25. angular-automatic-lock-bot commented on Jan 15, 2024

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  26. locked and limited conversation to collaborators on Jan 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: commonIssues related to APIs in the @angular/common packagearea: compilerIssues related to `ngc`, Angular's template compilercompiler: template type-checkingfeatureLabel used to distinguish feature request from other issuesfeature: under considerationFeature request for which voting has completed and the request is now under consideration

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions