Skip to content

bug(menu): Only deprecated constructor overloads are in the declaration file #24740

Closed
@ChristofferGersen

Description

@ChristofferGersen

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

Only deprecated constructor overloads are in the declaration file. As far as I can tell this is the case for the following classes:

  • MatMenu
  • MatMenuTrigger / _MatMenuTriggerBase
  • MatMenuContent / _MatMenuContentBase

For example, when I try to call the MatMenuTrigger constructor manually, my linter complains about "focusMonitor will become a required parameter". I am already specifying this parameter with a defined focusMonitor, so I would expect the error to be "ngZone will become a required parameter". This error is not given, because TypeScript matches overloads from top to bottom, so the most specific overload should be at the top.
Beside that the overload accepting an NgZone is not included in the declaration file, because it is only included in the implementation signature, see TypeScript Docs.

So to fix this for MatMenuTrigger the signatures should be listed as follows:

constructor(
    overlay: Overlay,
    element: ElementRef<HTMLElement>,
    viewContainerRef: ViewContainerRef,
    scrollStrategy: any,
    parentMenu: MatMenuPanel,
    menuItemInstance: MatMenuItem,
    dir: Directionality,
    focusMonitor: FocusMonitor,
    ngZone: NgZone,
  );

  /**
   * @deprecated `ngZone` will become a required parameter.
   * @breaking-change 15.0.0
   */
  constructor(
    overlay: Overlay,
    element: ElementRef<HTMLElement>,
    viewContainerRef: ViewContainerRef,
    scrollStrategy: any,
    parentMenu: MatMenuPanel,
    menuItemInstance: MatMenuItem,
    dir: Directionality,
    focusMonitor: FocusMonitor,
  );

  /**
   * @deprecated `focusMonitor` will become a required parameter.
   * @breaking-change 8.0.0
   */
  constructor(
    overlay: Overlay,
    element: ElementRef<HTMLElement>,
    viewContainerRef: ViewContainerRef,
    scrollStrategy: any,
    parentMenu: MatMenuPanel,
    menuItemInstance: MatMenuItem,
    dir: Directionality,
    focusMonitor?: FocusMonitor | null,
  );

  constructor(
    private _overlay: Overlay,
    private _element: ElementRef<HTMLElement>,
    private _viewContainerRef: ViewContainerRef,
    @Inject(MAT_MENU_SCROLL_STRATEGY) scrollStrategy: any,
    @Inject(MAT_MENU_PANEL) @Optional() parentMenu: MatMenuPanel,
    // `MatMenuTrigger` is commonly used in combination with a `MatMenuItem`.
    // tslint:disable-next-line: lightweight-tokens
    @Optional() @Self() private _menuItemInstance: MatMenuItem,
    @Optional() private _dir: Directionality,
    private _focusMonitor: FocusMonitor | null,
    private _ngZone?: NgZone,
  ) {
    // implementation
  }

Here I changed the order of the overloads and added the non-deprecated overload at the top.

Reproduction

StackBlitz

import { FocusMonitor } from "@angular/cdk/a11y";
import { Directionality } from "@angular/cdk/bidi";
import { Overlay } from "@angular/cdk/overlay";
import {
  AfterContentInit,
  AfterViewInit,
  Component,
  ElementRef,
  Inject,
  NgZone,
  Optional,
  ViewChild,
  ViewContainerRef,
} from "@angular/core";
import {
  MAT_MENU_SCROLL_STRATEGY,
  MatMenu,
  MatMenuItem,
  MatMenuPanel,
  MatMenuTrigger,
} from "@angular/material/menu";

@Component({
  selector: "example-component",
  template: "<mat-menu><div mat-menu-item>Item</div></mat-menu>",
})
export class ExampleComponent
  implements AfterContentInit, AfterViewInit
{
  @ViewChild(MatMenu, { static: true }) readonly menu?: MatMenu;

  private readonly trigger: MatMenuTrigger;

  constructor(
    overlay: Overlay,
    element: ElementRef<HTMLElement>,
    viewContainerRef: ViewContainerRef,
    @Inject(MAT_MENU_SCROLL_STRATEGY) scrollStrategy: any,
    @Optional() dir: Directionality,
    focusMonitor: FocusMonitor,
    ngZone: NgZone
  ) {
    this.trigger = new MatMenuTrigger(
      overlay,
      element,
      viewContainerRef,
      scrollStrategy,
      null as unknown as MatMenuPanel,
      null as unknown as MatMenuItem,
      dir,
      focusMonitor,
      ngZone
    );
  }

  ngAfterContentInit(): void {
    this.trigger.menu = this.menu as any;
    this.trigger.ngAfterContentInit();
    
  }

  ngAfterViewInit(): void {
    setTimeout(() => this.trigger.openMenu(), 0);
  }
}

Expected Behavior

The StackBlitz should compile

Actual Behavior

Got error:

Error in src/app/example-component.ts (52:7)
Expected 7-8 arguments, but got 9.

Environment

  • Angular: 13.3.1
  • CDK/Material: 13.3.2
  • Browser(s): Any (Edge/Chrome)
  • Operating System (e.g. Windows, macOS, Ubuntu): Any (Windows)

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: material/menu

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions