From ca7599802035fbaca3ae6ff10033236421efc094 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 10:04:51 +0200 Subject: [PATCH 01/35] test(multiple): more strict initialization failures in CDK tests Fixes some more strict initialization failures in the CDK that weren't caught in the previous PR. --- .../aria-describer/aria-describer.spec.ts | 8 ++-- .../a11y/focus-monitor/focus-monitor.spec.ts | 2 +- src/cdk/a11y/focus-trap/focus-trap.spec.ts | 18 +++---- src/cdk/accordion/accordion.spec.ts | 8 ++-- src/cdk/dialog/dialog.spec.ts | 12 ++--- src/cdk/menu/menu-group.spec.ts | 2 +- .../fullscreen-overlay-container.spec.ts | 2 +- src/cdk/overlay/overlay-container.spec.ts | 2 +- src/cdk/overlay/overlay-directives.spec.ts | 48 +++++++++---------- .../overlay/overlay-directives.zone.spec.ts | 44 ++++++++--------- src/cdk/overlay/overlay.spec.ts | 2 +- src/cdk/portal/portal.spec.ts | 16 +++---- src/cdk/scrolling/scroll-dispatcher.spec.ts | 6 +-- .../scrolling/scroll-dispatcher.zone.spec.ts | 4 +- src/cdk/scrolling/scrollable.spec.ts | 14 +++--- .../scrolling/virtual-scroll-viewport.spec.ts | 22 ++++----- .../virtual-scroll-viewport.zone.spec.ts | 4 +- src/cdk/testing/tests/test-main-component.ts | 10 ++-- src/cdk/text-field/autofill.spec.ts | 8 ++-- src/cdk/text-field/autosize.spec.ts | 2 +- 20 files changed, 117 insertions(+), 117 deletions(-) diff --git a/src/cdk/a11y/aria-describer/aria-describer.spec.ts b/src/cdk/a11y/aria-describer/aria-describer.spec.ts index 739f8e605e87..d3f73167b10e 100644 --- a/src/cdk/a11y/aria-describer/aria-describer.spec.ts +++ b/src/cdk/a11y/aria-describer/aria-describer.spec.ts @@ -400,22 +400,22 @@ function expectMessage(el: Element, message: string) { class TestApp { ariaDescriber = inject(AriaDescriber); - @ViewChild('element1') _element1: ElementRef; + @ViewChild('element1') _element1!: ElementRef; get element1(): Element { return this._element1.nativeElement; } - @ViewChild('element2') _element2: ElementRef; + @ViewChild('element2') _element2!: ElementRef; get element2(): Element { return this._element2.nativeElement; } - @ViewChild('element3') _element3: ElementRef; + @ViewChild('element3') _element3!: ElementRef; get element3(): Element { return this._element3.nativeElement; } - @ViewChild('element4') _element4: ElementRef; + @ViewChild('element4') _element4!: ElementRef; get element4(): Element { return this._element4.nativeElement; } diff --git a/src/cdk/a11y/focus-monitor/focus-monitor.spec.ts b/src/cdk/a11y/focus-monitor/focus-monitor.spec.ts index e2cc98c05010..ee2955d28b1f 100644 --- a/src/cdk/a11y/focus-monitor/focus-monitor.spec.ts +++ b/src/cdk/a11y/focus-monitor/focus-monitor.spec.ts @@ -920,5 +920,5 @@ class CheckboxWithLabel {} imports: [A11yModule], }) class ExportedFocusMonitor { - @ViewChild('exportedDir') exportedDirRef: CdkMonitorFocus; + @ViewChild('exportedDir') exportedDirRef!: CdkMonitorFocus; } diff --git a/src/cdk/a11y/focus-trap/focus-trap.spec.ts b/src/cdk/a11y/focus-trap/focus-trap.spec.ts index 428ca86c3337..6760eba821a6 100644 --- a/src/cdk/a11y/focus-trap/focus-trap.spec.ts +++ b/src/cdk/a11y/focus-trap/focus-trap.spec.ts @@ -325,7 +325,7 @@ function getActiveElement() { imports: [A11yModule, PortalModule], }) class SimpleFocusTrap { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; } const AUTO_FOCUS_TEMPLATE = ` @@ -343,7 +343,7 @@ const AUTO_FOCUS_TEMPLATE = ` imports: [A11yModule, PortalModule], }) class FocusTrapWithAutoCapture { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; showTrappedRegion = false; autoCaptureEnabled = true; } @@ -367,7 +367,7 @@ class FocusTrapWithAutoCaptureInShadowDom extends FocusTrapWithAutoCapture {} imports: [A11yModule, PortalModule], }) class FocusTrapWithBindings { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; renderFocusTrap = true; _isFocusTrapEnabled = true; } @@ -387,7 +387,7 @@ class FocusTrapWithBindings { imports: [A11yModule, PortalModule], }) class FocusTrapTargets { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; } @Component({ @@ -399,7 +399,7 @@ class FocusTrapTargets { imports: [A11yModule, PortalModule], }) class FocusTrapUnfocusableTarget { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; } @Component({ @@ -413,7 +413,7 @@ class FocusTrapUnfocusableTarget { imports: [A11yModule, PortalModule], }) class FocusTrapWithSvg { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; } @Component({ @@ -425,7 +425,7 @@ class FocusTrapWithSvg { imports: [A11yModule, PortalModule], }) class FocusTrapWithoutFocusableElements { - @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + @ViewChild(CdkTrapFocus) focusTrapDirective!: CdkTrapFocus; } @Component({ @@ -445,6 +445,6 @@ class FocusTrapWithoutFocusableElements { class FocusTrapInsidePortal { viewContainerRef = inject(ViewContainerRef); - @ViewChild('template') template: TemplateRef; - @ViewChild(CdkPortalOutlet) portalOutlet: CdkPortalOutlet; + @ViewChild('template') template!: TemplateRef; + @ViewChild(CdkPortalOutlet) portalOutlet!: CdkPortalOutlet; } diff --git a/src/cdk/accordion/accordion.spec.ts b/src/cdk/accordion/accordion.spec.ts index 024fe4f3ad01..fe0381eca803 100644 --- a/src/cdk/accordion/accordion.spec.ts +++ b/src/cdk/accordion/accordion.spec.ts @@ -111,8 +111,8 @@ describe('CdkAccordion', () => { imports: [CdkAccordionModule], }) class SetOfItems { - @ViewChild(CdkAccordion) accordion: CdkAccordion; - @ViewChildren(CdkAccordionItem) items: QueryList; + @ViewChild(CdkAccordion) accordion!: CdkAccordion; + @ViewChildren(CdkAccordionItem) items!: QueryList; multi: boolean = false; } @@ -126,6 +126,6 @@ class SetOfItems { imports: [CdkAccordionModule], }) class NestedItems { - @ViewChild('outerItem') outerItem: CdkAccordionItem; - @ViewChild('innerItem') innerItem: CdkAccordionItem; + @ViewChild('outerItem') outerItem!: CdkAccordionItem; + @ViewChild('innerItem') innerItem!: CdkAccordionItem; } diff --git a/src/cdk/dialog/dialog.spec.ts b/src/cdk/dialog/dialog.spec.ts index 1f9e65b3b891..1d67421c468f 100644 --- a/src/cdk/dialog/dialog.spec.ts +++ b/src/cdk/dialog/dialog.spec.ts @@ -1414,7 +1414,7 @@ class ComponentWithOnPushViewContainer { imports: [DirectiveWithViewContainer], }) class ComponentWithChildViewContainer { - @ViewChild(DirectiveWithViewContainer) childWithViewContainer: DirectiveWithViewContainer; + @ViewChild(DirectiveWithViewContainer) childWithViewContainer!: DirectiveWithViewContainer; get childViewContainer() { return this.childWithViewContainer.viewContainerRef; @@ -1428,10 +1428,10 @@ class ComponentWithChildViewContainer { imports: [DialogModule], }) class ComponentWithTemplateRef { - localValue: string; - dialogRef: DialogRef; + localValue = ''; + dialogRef: DialogRef | undefined; - @ViewChild(TemplateRef) templateRef: TemplateRef; + @ViewChild(TemplateRef) templateRef!: TemplateRef; setDialogRef(dialogRef: DialogRef): string { this.dialogRef = dialogRef; @@ -1461,7 +1461,7 @@ class PizzaMsg { }, }) class ContentElementDialog { - closeButtonAriaLabel: string; + closeButtonAriaLabel = ''; } @Component({ @@ -1521,6 +1521,6 @@ class TemplateInjectorInnerDirective { imports: [TemplateInjectorInnerDirective], }) class TemplateInjectorParentComponent { - @ViewChild(TemplateRef) templateRef: TemplateRef; + @ViewChild(TemplateRef) templateRef!: TemplateRef; innerComponentValue = ''; } diff --git a/src/cdk/menu/menu-group.spec.ts b/src/cdk/menu/menu-group.spec.ts index 46bfecb48f80..2b41d725fe11 100644 --- a/src/cdk/menu/menu-group.spec.ts +++ b/src/cdk/menu/menu-group.spec.ts @@ -142,5 +142,5 @@ class CheckboxMenu { imports: [CdkMenuModule], }) class MenuWithMultipleRadioGroups { - @ViewChild(CdkMenuItem) readonly trigger: CdkMenuItem; + @ViewChild(CdkMenuItem) readonly trigger!: CdkMenuItem; } diff --git a/src/cdk/overlay/fullscreen-overlay-container.spec.ts b/src/cdk/overlay/fullscreen-overlay-container.spec.ts index 3ee762da3665..2622a904da87 100644 --- a/src/cdk/overlay/fullscreen-overlay-container.spec.ts +++ b/src/cdk/overlay/fullscreen-overlay-container.spec.ts @@ -111,5 +111,5 @@ describe('FullscreenOverlayContainer', () => { class TestComponentWithTemplatePortals { viewContainerRef = inject(ViewContainerRef); - @ViewChild(CdkPortal) templatePortal: CdkPortal; + @ViewChild(CdkPortal) templatePortal!: CdkPortal; } diff --git a/src/cdk/overlay/overlay-container.spec.ts b/src/cdk/overlay/overlay-container.spec.ts index 04af6b0691c7..0ac658aa18ef 100644 --- a/src/cdk/overlay/overlay-container.spec.ts +++ b/src/cdk/overlay/overlay-container.spec.ts @@ -87,5 +87,5 @@ describe('OverlayContainer', () => { class TestComponentWithTemplatePortals { viewContainerRef = inject(ViewContainerRef); - @ViewChild(CdkPortal) templatePortal: CdkPortal; + @ViewChild(CdkPortal) templatePortal!: CdkPortal; } diff --git a/src/cdk/overlay/overlay-directives.spec.ts b/src/cdk/overlay/overlay-directives.spec.ts index ef11a388e13a..c704b29dc700 100644 --- a/src/cdk/overlay/overlay-directives.spec.ts +++ b/src/cdk/overlay/overlay-directives.spec.ts @@ -791,38 +791,38 @@ describe('Overlay directives', () => { imports: [OverlayModule], }) class ConnectedOverlayDirectiveTest { - @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; - @ViewChild('trigger') trigger: CdkOverlayOrigin; - @ViewChild('otherTrigger') otherTrigger: CdkOverlayOrigin; - @ViewChild('nonDirectiveTrigger') nonDirectiveTrigger: ElementRef; + @ViewChild(CdkConnectedOverlay) connectedOverlayDirective!: CdkConnectedOverlay; + @ViewChild('trigger') trigger!: CdkOverlayOrigin; + @ViewChild('otherTrigger') otherTrigger!: CdkOverlayOrigin; + @ViewChild('nonDirectiveTrigger') nonDirectiveTrigger!: ElementRef; isOpen = false; - width: number | string; - height: number | string; - minWidth: number | string; - positionStrategy: FlexibleConnectedPositionStrategy; - minHeight: number | string; - offsetX: number; - offsetY: number; - triggerOverride: CdkOverlayOrigin | FlexibleConnectedPositionStrategyOrigin; - hasBackdrop: boolean; - disableClose: boolean; - viewportMargin: number; - flexibleDimensions: boolean; - growAfterOpen: boolean; - push: boolean; - scrollStrategy: ScrollStrategy; + width!: number | string; + height!: number | string; + minWidth!: number | string; + positionStrategy!: FlexibleConnectedPositionStrategy; + minHeight!: number | string; + offsetX!: number; + offsetY!: number; + triggerOverride!: CdkOverlayOrigin | FlexibleConnectedPositionStrategyOrigin; + hasBackdrop!: boolean; + disableClose!: boolean; + viewportMargin!: number; + flexibleDimensions!: boolean; + growAfterOpen!: boolean; + push!: boolean; + scrollStrategy!: ScrollStrategy; backdropClickHandler = jasmine.createSpy('backdropClick handler'); positionChangeHandler = jasmine.createSpy('positionChange handler'); keydownHandler = jasmine.createSpy('keydown handler'); - positionOverrides: ConnectionPositionPair[]; + positionOverrides!: ConnectionPositionPair[]; attachHandler = jasmine.createSpy('attachHandler').and.callFake(() => { const overlayElement = this.connectedOverlayDirective.overlayRef.overlayElement; this.attachResult = overlayElement.querySelector('p') as HTMLElement; }); detachHandler = jasmine.createSpy('detachHandler'); - attachResult: HTMLElement; - transformOriginSelector: string; + attachResult!: HTMLElement; + transformOriginSelector!: string; matchWidth = false; } @@ -834,6 +834,6 @@ class ConnectedOverlayDirectiveTest { imports: [OverlayModule], }) class ConnectedOverlayPropertyInitOrder { - @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; - @ViewChild('trigger') trigger: CdkOverlayOrigin; + @ViewChild(CdkConnectedOverlay) connectedOverlayDirective!: CdkConnectedOverlay; + @ViewChild('trigger') trigger!: CdkOverlayOrigin; } diff --git a/src/cdk/overlay/overlay-directives.zone.spec.ts b/src/cdk/overlay/overlay-directives.zone.spec.ts index 53907bac554d..8c2e792aed2f 100644 --- a/src/cdk/overlay/overlay-directives.zone.spec.ts +++ b/src/cdk/overlay/overlay-directives.zone.spec.ts @@ -77,36 +77,36 @@ describe('Overlay directives Zone.js integration', () => { imports: [OverlayModule], }) class ConnectedOverlayDirectiveTest { - @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; - @ViewChild('trigger') trigger: CdkOverlayOrigin; - @ViewChild('otherTrigger') otherTrigger: CdkOverlayOrigin; - @ViewChild('nonDirectiveTrigger') nonDirectiveTrigger: ElementRef; + @ViewChild(CdkConnectedOverlay) connectedOverlayDirective!: CdkConnectedOverlay; + @ViewChild('trigger') trigger!: CdkOverlayOrigin; + @ViewChild('otherTrigger') otherTrigger!: CdkOverlayOrigin; + @ViewChild('nonDirectiveTrigger') nonDirectiveTrigger!: ElementRef; isOpen = false; - width: number | string; - height: number | string; - minWidth: number | string; - positionStrategy: FlexibleConnectedPositionStrategy; - minHeight: number | string; - offsetX: number; - offsetY: number; - triggerOverride: CdkOverlayOrigin | FlexibleConnectedPositionStrategyOrigin; - hasBackdrop: boolean; - disableClose: boolean; - viewportMargin: number; - flexibleDimensions: boolean; - growAfterOpen: boolean; - push: boolean; - scrollStrategy: ScrollStrategy; + width!: number | string; + height!: number | string; + minWidth!: number | string; + positionStrategy!: FlexibleConnectedPositionStrategy; + minHeight!: number | string; + offsetX!: number; + offsetY!: number; + triggerOverride!: CdkOverlayOrigin | FlexibleConnectedPositionStrategyOrigin; + hasBackdrop!: boolean; + disableClose!: boolean; + viewportMargin!: number; + flexibleDimensions!: boolean; + growAfterOpen!: boolean; + push!: boolean; + scrollStrategy!: ScrollStrategy; backdropClickHandler = jasmine.createSpy('backdropClick handler'); positionChangeHandler = jasmine.createSpy('positionChange handler'); keydownHandler = jasmine.createSpy('keydown handler'); - positionOverrides: ConnectionPositionPair[]; + positionOverrides!: ConnectionPositionPair[]; attachHandler = jasmine.createSpy('attachHandler').and.callFake(() => { const overlayElement = this.connectedOverlayDirective.overlayRef.overlayElement; this.attachResult = overlayElement.querySelector('p') as HTMLElement; }); detachHandler = jasmine.createSpy('detachHandler'); - attachResult: HTMLElement; - transformOriginSelector: string; + attachResult!: HTMLElement; + transformOriginSelector!: string; } diff --git a/src/cdk/overlay/overlay.spec.ts b/src/cdk/overlay/overlay.spec.ts index c2c0ac0aebd5..3a529b2051ab 100644 --- a/src/cdk/overlay/overlay.spec.ts +++ b/src/cdk/overlay/overlay.spec.ts @@ -1131,7 +1131,7 @@ class PizzaMsg {} class TestComponentWithTemplatePortals { viewContainerRef = inject(ViewContainerRef); - @ViewChild(CdkPortal) templatePortal: CdkPortal; + @ViewChild(CdkPortal) templatePortal!: CdkPortal; } class FakePositionStrategy implements PositionStrategy { diff --git a/src/cdk/portal/portal.spec.ts b/src/cdk/portal/portal.spec.ts index 8726f014d4f0..c7159875b4b7 100644 --- a/src/cdk/portal/portal.spec.ts +++ b/src/cdk/portal/portal.spec.ts @@ -779,8 +779,8 @@ class ArbitraryViewContainerRefComponent { viewContainerRef = inject(ViewContainerRef); injector = inject(Injector); - @ViewChild('template') template: TemplateRef; - @ViewChild(SaveParentNodeOnInit) saveParentNodeOnInit: SaveParentNodeOnInit; + @ViewChild('template') template!: TemplateRef; + @ViewChild(SaveParentNodeOnInit) saveParentNodeOnInit!: SaveParentNodeOnInit; } /** Test-bed component that contains a portal outlet and a couple of template portals. */ @@ -820,12 +820,12 @@ class PortalTestApp { viewContainerRef = inject(ViewContainerRef); injector = inject(Injector); - @ViewChildren(CdkPortal) portals: QueryList; - @ViewChild(CdkPortalOutlet) portalOutlet: CdkPortalOutlet; - @ViewChild('templateRef', {read: TemplateRef}) templateRef: TemplateRef; - @ViewChild('domPortalContent') domPortalContent: ElementRef; + @ViewChildren(CdkPortal) portals!: QueryList; + @ViewChild(CdkPortalOutlet) portalOutlet!: CdkPortalOutlet; + @ViewChild('templateRef', {read: TemplateRef}) templateRef!: TemplateRef; + @ViewChild('domPortalContent') domPortalContent!: ElementRef; @ViewChild('alternateContainer', {read: ViewContainerRef}) - alternateContainer: ViewContainerRef; + alternateContainer!: ViewContainerRef; selectedPortal: Portal | undefined; fruit: string = 'Banana'; @@ -859,5 +859,5 @@ class PortalTestApp { imports: [CdkPortalOutlet], }) class UnboundPortalTestApp { - @ViewChild(CdkPortalOutlet) portalOutlet: CdkPortalOutlet; + @ViewChild(CdkPortalOutlet) portalOutlet!: CdkPortalOutlet; } diff --git a/src/cdk/scrolling/scroll-dispatcher.spec.ts b/src/cdk/scrolling/scroll-dispatcher.spec.ts index 6d980e639d9c..eca90f19a365 100644 --- a/src/cdk/scrolling/scroll-dispatcher.spec.ts +++ b/src/cdk/scrolling/scroll-dispatcher.spec.ts @@ -261,8 +261,8 @@ describe('ScrollDispatcher', () => { imports: [ScrollingModule], }) class ScrollingComponent { - @ViewChild(CdkScrollable) scrollable: CdkScrollable; - @ViewChild('scrollingElement') scrollingElement: ElementRef; + @ViewChild(CdkScrollable) scrollable!: CdkScrollable; + @ViewChild('scrollingElement') scrollingElement!: ElementRef; } /** Component containing nested scrollables. */ @@ -279,5 +279,5 @@ class ScrollingComponent { imports: [ScrollingModule], }) class NestedScrollingComponent { - @ViewChild('interestingElement') interestingElement: ElementRef; + @ViewChild('interestingElement') interestingElement!: ElementRef; } diff --git a/src/cdk/scrolling/scroll-dispatcher.zone.spec.ts b/src/cdk/scrolling/scroll-dispatcher.zone.spec.ts index cff6030c2c96..676c5155990d 100644 --- a/src/cdk/scrolling/scroll-dispatcher.zone.spec.ts +++ b/src/cdk/scrolling/scroll-dispatcher.zone.spec.ts @@ -38,6 +38,6 @@ describe('ScrollDispatcher Zone.js integration', () => { imports: [ScrollingModule], }) class ScrollingComponent { - @ViewChild(CdkScrollable) scrollable: CdkScrollable; - @ViewChild('scrollingElement') scrollingElement: ElementRef; + @ViewChild(CdkScrollable) scrollable!: CdkScrollable; + @ViewChild('scrollingElement') scrollingElement!: ElementRef; } diff --git a/src/cdk/scrolling/scrollable.spec.ts b/src/cdk/scrolling/scrollable.spec.ts index 7cc6ec4b4573..950d3a347e52 100644 --- a/src/cdk/scrolling/scrollable.spec.ts +++ b/src/cdk/scrolling/scrollable.spec.ts @@ -243,11 +243,11 @@ describe('CdkScrollable', () => { imports: [ScrollingModule], }) class ScrollableViewport { - @Input() dir: Direction; - @ViewChild(CdkScrollable) scrollable: CdkScrollable; - @ViewChild('scrollContainer') scrollContainer: ElementRef; - @ViewChild('firstRowStart') firstRowStart: ElementRef; - @ViewChild('firstRowEnd') firstRowEnd: ElementRef; - @ViewChild('lastRowStart') lastRowStart: ElementRef; - @ViewChild('lastRowEnd') lastRowEnd: ElementRef; + @Input() dir: Direction = 'ltr'; + @ViewChild(CdkScrollable) scrollable!: CdkScrollable; + @ViewChild('scrollContainer') scrollContainer!: ElementRef; + @ViewChild('firstRowStart') firstRowStart!: ElementRef; + @ViewChild('firstRowEnd') firstRowEnd!: ElementRef; + @ViewChild('lastRowStart') lastRowStart!: ElementRef; + @ViewChild('lastRowEnd') lastRowEnd!: ElementRef; } diff --git a/src/cdk/scrolling/virtual-scroll-viewport.spec.ts b/src/cdk/scrolling/virtual-scroll-viewport.spec.ts index 473651317913..bfbb970cbd21 100644 --- a/src/cdk/scrolling/virtual-scroll-viewport.spec.ts +++ b/src/cdk/scrolling/virtual-scroll-viewport.spec.ts @@ -1192,9 +1192,9 @@ function triggerScroll(viewport: CdkVirtualScrollViewport, offset?: number) { imports: [ScrollingModule], }) class FixedSizeVirtualScroll { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; // Casting virtualForOf as any so we can spy on private methods - @ViewChild(CdkVirtualForOf, {static: true}) virtualForOf: any; + @ViewChild(CdkVirtualForOf, {static: true}) virtualForOf!: any; orientation: 'vertical' | 'horizontal' = 'vertical'; viewportSize = 200; @@ -1205,7 +1205,7 @@ class FixedSizeVirtualScroll { items = Array(10) .fill(0) .map((_, i) => i); - trackBy: TrackByFunction; + trackBy!: TrackByFunction; templateCacheSize = 20; scrolledToIndex = 0; @@ -1257,7 +1257,7 @@ class FixedSizeVirtualScroll { imports: [ScrollingModule], }) class FixedSizeVirtualScrollWithRtlDirection { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; orientation: 'vertical' | 'horizontal' = 'vertical'; viewportSize = 200; @@ -1268,7 +1268,7 @@ class FixedSizeVirtualScrollWithRtlDirection { items = Array(10) .fill(0) .map((_, i) => i); - trackBy: TrackByFunction; + trackBy!: TrackByFunction; templateCacheSize = 20; scrolledToIndex = 0; @@ -1340,7 +1340,7 @@ class InjectsViewContainer { imports: [InjectsViewContainer, ScrollingModule], }) class VirtualScrollWithItemInjectingViewContainer { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; itemSize = 50; items = Array(20000) .fill(0) @@ -1378,7 +1378,7 @@ class VirtualScrollWithItemInjectingViewContainer { imports: [ScrollingModule], }) class DelayedInitializationVirtualScroll { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; itemSize = 50; items = Array(20000) .fill(0) @@ -1416,7 +1416,7 @@ class DelayedInitializationVirtualScroll { imports: [ScrollingModule], }) class VirtualScrollWithAppendOnly { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; itemSize = 50; items = Array(20000) .fill(0) @@ -1458,7 +1458,7 @@ class VirtualScrollWithAppendOnly { imports: [ScrollingModule], }) class VirtualScrollWithCustomScrollingElement { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; itemSize = 50; items = Array(20000) .fill(0) @@ -1499,7 +1499,7 @@ class VirtualScrollWithCustomScrollingElement { imports: [ScrollingModule], }) class VirtualScrollWithScrollableWindow { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; itemSize = 50; items = Array(20000) .fill(0) @@ -1511,5 +1511,5 @@ class VirtualScrollWithScrollableWindow { imports: [ScrollingModule], }) class VirtualScrollableQuery { - @ViewChild(CdkScrollable) scrollable: CdkScrollable; + @ViewChild(CdkScrollable) scrollable!: CdkScrollable; } diff --git a/src/cdk/scrolling/virtual-scroll-viewport.zone.spec.ts b/src/cdk/scrolling/virtual-scroll-viewport.zone.spec.ts index ff61318b4c20..a36ca5b5494c 100644 --- a/src/cdk/scrolling/virtual-scroll-viewport.zone.spec.ts +++ b/src/cdk/scrolling/virtual-scroll-viewport.zone.spec.ts @@ -101,7 +101,7 @@ describe('CdkVirtualScrollViewport Zone.js intergation', () => { imports: [ScrollingModule], }) class FixedSizeVirtualScroll { - @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport; + @ViewChild(CdkVirtualScrollViewport, {static: true}) viewport!: CdkVirtualScrollViewport; // Casting virtualForOf as any so we can spy on private methods @ViewChild(CdkVirtualForOf, {static: true}) virtualForOf: any; @@ -114,7 +114,7 @@ class FixedSizeVirtualScroll { items = Array(10) .fill(0) .map((_, i) => i); - trackBy: TrackByFunction; + trackBy!: TrackByFunction; templateCacheSize = 20; scrolledToIndex = 0; diff --git a/src/cdk/testing/tests/test-main-component.ts b/src/cdk/testing/tests/test-main-component.ts index 243c423bb09d..502bcbec3912 100644 --- a/src/cdk/testing/tests/test-main-component.ts +++ b/src/cdk/testing/tests/test-main-component.ts @@ -37,15 +37,15 @@ export class TestMainComponent implements OnDestroy { username: string; counter: number; asyncCounter: number; - input: string; + input!: string; memo: string; testTools: string[]; testMethods: string[]; isHovering = false; isPointerOver = false; specialKey = ''; - modifiers: string; - singleSelect: string; + modifiers!: string; + singleSelect!: string; singleSelectChangeEventCount = 0; multiSelect: string[] = []; multiSelectChangeEventCount = 0; @@ -56,8 +56,8 @@ export class TestMainComponent implements OnDestroy { rightClickResult = {x: -1, y: -1, button: -1}; numberControl = new FormControl(null); - @ViewChild('clickTestElement') clickTestElement: ElementRef; - @ViewChild('taskStateResult') taskStateResultElement: ElementRef; + @ViewChild('clickTestElement') clickTestElement!: ElementRef; + @ViewChild('taskStateResult') taskStateResultElement!: ElementRef; private _fakeOverlayElement: HTMLElement; diff --git a/src/cdk/text-field/autofill.spec.ts b/src/cdk/text-field/autofill.spec.ts index 1c91d44d0e41..b8f0e94ccecc 100644 --- a/src/cdk/text-field/autofill.spec.ts +++ b/src/cdk/text-field/autofill.spec.ts @@ -196,9 +196,9 @@ describe('cdkAutofill', () => { }) class Inputs { // Cast to `any` so we can stub out some methods in the tests. - @ViewChild('input1') input1: ElementRef; - @ViewChild('input2') input2: ElementRef; - @ViewChild('input3') input3: ElementRef; + @ViewChild('input1') input1!: ElementRef; + @ViewChild('input2') input2!: ElementRef; + @ViewChild('input3') input3!: ElementRef; } @Component({ @@ -207,5 +207,5 @@ class Inputs { }) class InputWithCdkAutofilled { // Cast to `any` so we can stub out some methods in the tests. - @ViewChild('input') input: ElementRef; + @ViewChild('input') input!: ElementRef; } diff --git a/src/cdk/text-field/autosize.spec.ts b/src/cdk/text-field/autosize.spec.ts index d469b4ab0012..4181fdd50f55 100644 --- a/src/cdk/text-field/autosize.spec.ts +++ b/src/cdk/text-field/autosize.spec.ts @@ -390,7 +390,7 @@ const textareaStyleReset = ` imports: [FormsModule, TextFieldModule], }) class AutosizeTextAreaWithContent { - @ViewChild('autosize') autosize: CdkTextareaAutosize; + @ViewChild('autosize') autosize!: CdkTextareaAutosize; minRows: number | null = null; maxRows: number | null = null; content: string = ''; From 327677d36cb7e9aaa38da8233822c0d4ef213e1a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 10:58:44 +0200 Subject: [PATCH 02/35] refactor(material/autocomplete): fix strict property initialization errors Updates the code to be compatible with strict property initialization. --- .../autocomplete/autocomplete-trigger.ts | 28 +++--- .../autocomplete/autocomplete.spec.ts | 86 +++++++++---------- src/material/autocomplete/autocomplete.ts | 24 +++--- .../autocomplete/autocomplete.zone.spec.ts | 20 ++--- 4 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/material/autocomplete/autocomplete-trigger.ts b/src/material/autocomplete/autocomplete-trigger.ts index 57406381c01c..a5bfdb0386f3 100644 --- a/src/material/autocomplete/autocomplete-trigger.ts +++ b/src/material/autocomplete/autocomplete-trigger.ts @@ -140,31 +140,31 @@ export class MatAutocompleteTrigger {optional: true}, ); - private _overlayRef: OverlayRef | null; - private _portal: TemplatePortal; + private _overlayRef: OverlayRef | null = null; + private _portal!: TemplatePortal; private _componentDestroyed = false; private _initialized = new Subject(); - private _keydownSubscription: Subscription | null; - private _outsideClickSubscription: Subscription | null; + private _keydownSubscription: Subscription | undefined; + private _outsideClickSubscription: Subscription | undefined; private _cleanupWindowBlur: (() => void) | undefined; /** Old value of the native input. Used to work around issues with the `input` event on IE. */ - private _previousValue: string | number | null; + private _previousValue: string | number | null = null; /** Value of the input element when the panel was attached (even if there are no options). */ - private _valueOnAttach: string | number | null; + private _valueOnAttach: string | number | null = null; /** Value on the previous keydown event. */ - private _valueOnLastKeydown: string | null; + private _valueOnLastKeydown: string | null = null; /** Strategy that is used to position the panel. */ - private _positionStrategy: FlexibleConnectedPositionStrategy; + private _positionStrategy!: FlexibleConnectedPositionStrategy; /** Whether or not the label state is being overridden. */ private _manuallyFloatingLabel = false; /** The subscription for closing actions (some are bound to document). */ - private _closingActionsSubscription: Subscription; + private _closingActionsSubscription!: Subscription; /** Subscription to viewport size changes. */ private _viewportSubscription = Subscription.EMPTY; @@ -187,7 +187,7 @@ export class MatAutocompleteTrigger * Current option that we have auto-selected as the user is navigating, * but which hasn't been propagated to the model value yet. */ - private _pendingAutoselectedOption: MatOption | null; + private _pendingAutoselectedOption: MatOption | null = null; /** Stream of keyboard events that can close the panel. */ private readonly _closeKeyEventStream = new Subject(); @@ -213,7 +213,7 @@ export class MatAutocompleteTrigger _onTouched = () => {}; /** The autocomplete panel to be attached to this trigger. */ - @Input('matAutocomplete') autocomplete: MatAutocomplete; + @Input('matAutocomplete') autocomplete!: MatAutocomplete; /** * Position of the autocomplete panel relative to the trigger element. A position of `auto` @@ -228,7 +228,7 @@ export class MatAutocompleteTrigger * Reference relative to which to position the autocomplete panel. * Defaults to the autocomplete trigger element. */ - @Input('matAutocompleteConnectedTo') connectedTo: MatAutocompleteOrigin; + @Input('matAutocompleteConnectedTo') connectedTo!: MatAutocompleteOrigin; /** * `autocomplete` attribute to be set on the input element. @@ -241,7 +241,7 @@ export class MatAutocompleteTrigger * act as a regular input and the user won't be able to open the panel. */ @Input({alias: 'matAutocompleteDisabled', transform: booleanAttribute}) - autocompleteDisabled: boolean; + autocompleteDisabled: boolean = false; constructor(...args: unknown[]); constructor() {} @@ -887,7 +887,7 @@ export class MatAutocompleteTrigger } else { this._keydownSubscription?.unsubscribe(); this._outsideClickSubscription?.unsubscribe(); - this._keydownSubscription = this._outsideClickSubscription = null; + this._keydownSubscription = this._outsideClickSubscription = undefined; } } diff --git a/src/material/autocomplete/autocomplete.spec.ts b/src/material/autocomplete/autocomplete.spec.ts index bcec42255607..74ee062c7139 100644 --- a/src/material/autocomplete/autocomplete.spec.ts +++ b/src/material/autocomplete/autocomplete.spec.ts @@ -4053,26 +4053,26 @@ const SIMPLE_AUTOCOMPLETE_TEMPLATE = ` }) class SimpleAutocomplete implements OnDestroy { stateCtrl = new FormControl<{name: string; code: string} | string | null>(null); - filteredStates: any[]; - valueSub: Subscription; + filteredStates!: any[]; + valueSub!: Subscription; floatLabel: FloatLabelType = 'auto'; position: 'auto' | 'above' | 'below' = 'auto'; - width: number; + width!: number; disableRipple = false; autocompleteDisabled = false; hasLabel = true; requireSelection = false; - ariaLabel: string; - ariaLabelledby: string; + ariaLabel!: string; + ariaLabelledby!: string; panelClass = 'class-one class-two'; - theme: ThemePalette; + theme!: ThemePalette; openedSpy = jasmine.createSpy('autocomplete opened spy'); closedSpy = jasmine.createSpy('autocomplete closed spy'); - @ViewChild(MatAutocompleteTrigger, {static: true}) trigger: MatAutocompleteTrigger; - @ViewChild(MatAutocomplete) panel: MatAutocomplete; - @ViewChild(MatFormField) formField: MatFormField; - @ViewChildren(MatOption) options: QueryList; + @ViewChild(MatAutocompleteTrigger, {static: true}) trigger!: MatAutocompleteTrigger; + @ViewChild(MatAutocomplete) panel!: MatAutocomplete; + @ViewChild(MatFormField) formField!: MatFormField; + @ViewChildren(MatOption) options!: QueryList; states: {code: string; name: string; height?: number; disabled?: boolean}[] = [ {code: 'AL', name: 'Alabama'}, @@ -4146,12 +4146,12 @@ class SimpleAutocompleteShadowDom extends SimpleAutocomplete {} }) class NgIfAutocomplete { optionCtrl = new FormControl(''); - filteredOptions: Observable; + filteredOptions!: Observable; isVisible = true; options = ['One', 'Two', 'Three']; - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - @ViewChildren(MatOption) matOptions: QueryList; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + @ViewChildren(MatOption) matOptions!: QueryList; constructor() { this.filteredOptions = this.optionCtrl.valueChanges.pipe( @@ -4183,7 +4183,7 @@ class NgIfAutocomplete { imports: [MatAutocomplete, MatAutocompleteTrigger, MatOption, MatInputModule], }) class AutocompleteWithoutForms { - filteredStates: any[]; + filteredStates!: any[]; states = ['Alabama', 'California', 'Florida']; constructor() { @@ -4213,11 +4213,11 @@ class AutocompleteWithoutForms { imports: [MatAutocomplete, MatAutocompleteTrigger, MatOption, MatInputModule, FormsModule], }) class AutocompleteWithNgModel { - filteredStates: any[]; - selectedState: string; + filteredStates!: any[]; + selectedState!: string; states = ['New York', 'Washington', 'Oregon']; - @ViewChild(MatAutocompleteTrigger, {static: true}) trigger: MatAutocompleteTrigger; + @ViewChild(MatAutocompleteTrigger, {static: true}) trigger!: MatAutocompleteTrigger; constructor() { this.filteredStates = this.states.slice(); @@ -4245,7 +4245,7 @@ class AutocompleteWithNgModel { imports: [MatAutocomplete, MatAutocompleteTrigger, MatOption, MatInputModule, FormsModule], }) class AutocompleteWithNumbers { - selectedNumber: number; + selectedNumber!: number; numbers = [0, 1, 2]; } @@ -4271,8 +4271,8 @@ class AutocompleteWithNumbers { ], }) class AutocompleteWithOnPushDelay implements OnInit { - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - options: string[]; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + options!: string[]; ngOnInit() { setTimeout(() => { @@ -4295,11 +4295,11 @@ class AutocompleteWithOnPushDelay implements OnInit { }) class AutocompleteWithNativeInput { optionCtrl = new FormControl(''); - filteredOptions: Observable; + filteredOptions!: Observable; options = ['En', 'To', 'Tre', 'Fire', 'Fem']; - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - @ViewChildren(MatOption) matOptions: QueryList; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + @ViewChildren(MatOption) matOptions!: QueryList; constructor() { this.filteredOptions = this.optionCtrl.valueChanges.pipe( @@ -4318,7 +4318,7 @@ class AutocompleteWithNativeInput { imports: [MatAutocompleteTrigger, ReactiveFormsModule], }) class AutocompleteWithoutPanel { - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; control = new FormControl(''); } @@ -4350,8 +4350,8 @@ class AutocompleteWithoutPanel { ], }) class AutocompleteWithGroups { - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - selectedState: string; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + selectedState!: string; stateGroups = [ { title: 'One', @@ -4416,12 +4416,12 @@ class AutocompleteWithIndirectGroups extends AutocompleteWithGroups {} imports: [MatAutocomplete, MatAutocompleteTrigger, MatOption, MatInputModule, FormsModule], }) class AutocompleteWithSelectEvent { - selectedState: string; + selectedState!: string; states = ['New York', 'Washington', 'Oregon']; optionSelected = jasmine.createSpy('optionSelected callback'); - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - @ViewChild(MatAutocomplete) autocomplete: MatAutocomplete; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + @ViewChild(MatAutocomplete) autocomplete!: MatAutocomplete; } @Component({ @@ -4450,7 +4450,7 @@ class PlainAutocompleteInputWithFormControl { imports: [MatAutocomplete, MatAutocompleteTrigger, MatOption, MatInputModule, FormsModule], }) class AutocompleteWithNumberInputAndNgModel { - selectedValue: number; + selectedValue!: number; values = [1, 2, 3]; } @@ -4490,11 +4490,11 @@ class AutocompleteWithNumberInputAndNgModel { ], }) class AutocompleteWithDifferentOrigin { - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - @ViewChild(MatAutocompleteOrigin) alternateOrigin: MatAutocompleteOrigin; - selectedValue: string; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + @ViewChild(MatAutocompleteOrigin) alternateOrigin!: MatAutocompleteOrigin; + selectedValue!: string; values = ['one', 'two', 'three']; - connectedTo: MatAutocompleteOrigin; + connectedTo!: MatAutocompleteOrigin; } @Component({ @@ -4505,7 +4505,7 @@ class AutocompleteWithDifferentOrigin { imports: [MatAutocomplete, MatAutocompleteTrigger, FormsModule], }) class AutocompleteWithNativeAutocompleteAttribute { - value: string; + value!: string; } @Component({ @@ -4532,9 +4532,9 @@ class AutocompleteWithActivatedEvent { states = ['California', 'West Virginia', 'Florida']; optionActivated = jasmine.createSpy('optionActivated callback'); - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - @ViewChild(MatAutocomplete) autocomplete: MatAutocomplete; - @ViewChildren(MatOption) options: QueryList; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + @ViewChild(MatAutocomplete) autocomplete!: MatAutocomplete; + @ViewChildren(MatOption) options!: QueryList; } @Component({ @@ -4573,10 +4573,10 @@ class AutocompleteInsideAModal { formControl = new FormControl(); - @ViewChild(MatAutocomplete) autocomplete: MatAutocomplete; - @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger; - @ViewChildren(MatOption) options: QueryList; - @ViewChild('modal') modal: ElementRef; + @ViewChild(MatAutocomplete) autocomplete!: MatAutocomplete; + @ViewChild(MatAutocompleteTrigger) trigger!: MatAutocompleteTrigger; + @ViewChildren(MatOption) options!: QueryList; + @ViewChild('modal') modal!: ElementRef; } @Component({ @@ -4591,5 +4591,5 @@ class AutocompleteInsideAModal { imports: [MatAutocomplete, MatAutocompleteTrigger, MatInputModule], }) class AutocompleteWithoutOptions { - @ViewChild(MatAutocompleteTrigger, {static: true}) trigger: MatAutocompleteTrigger; + @ViewChild(MatAutocompleteTrigger, {static: true}) trigger!: MatAutocompleteTrigger; } diff --git a/src/material/autocomplete/autocomplete.ts b/src/material/autocomplete/autocomplete.ts index 82b5be53389e..44dec71748ae 100644 --- a/src/material/autocomplete/autocomplete.ts +++ b/src/material/autocomplete/autocomplete.ts @@ -119,7 +119,7 @@ export class MatAutocomplete implements AfterContentInit, OnDestroy { private _activeOptionChanges = Subscription.EMPTY; /** Manages active item in option list based on key events. */ - _keyManager: ActiveDescendantKeyManager; + _keyManager!: ActiveDescendantKeyManager; /** Whether the autocomplete panel should be visible, depending on option length. */ showPanel: boolean = false; @@ -131,7 +131,7 @@ export class MatAutocomplete implements AfterContentInit, OnDestroy { _isOpen: boolean = false; /** Latest trigger that opened the autocomplete. */ - _latestOpeningTrigger: unknown; + _latestOpeningTrigger!: unknown; /** @docs-private Sets the theme color of the panel. */ _setColor(value: ThemePalette) { @@ -139,29 +139,29 @@ export class MatAutocomplete implements AfterContentInit, OnDestroy { this._changeDetectorRef.markForCheck(); } /** @docs-private theme color of the panel */ - protected _color: ThemePalette; + protected _color!: ThemePalette; // The @ViewChild query for TemplateRef here needs to be static because some code paths // lead to the overlay being created before change detection has finished for this component. // Notably, another component may trigger `focus` on the autocomplete-trigger. /** @docs-private */ - @ViewChild(TemplateRef, {static: true}) template: TemplateRef; + @ViewChild(TemplateRef, {static: true}) template!: TemplateRef; /** Element for the panel containing the autocomplete options. */ - @ViewChild('panel') panel: ElementRef; + @ViewChild('panel') panel!: ElementRef; /** Reference to all options within the autocomplete. */ - @ContentChildren(MatOption, {descendants: true}) options: QueryList; + @ContentChildren(MatOption, {descendants: true}) options!: QueryList; /** Reference to all option groups within the autocomplete. */ - @ContentChildren(MAT_OPTGROUP, {descendants: true}) optionGroups: QueryList; + @ContentChildren(MAT_OPTGROUP, {descendants: true}) optionGroups!: QueryList; /** Aria label of the autocomplete. */ - @Input('aria-label') ariaLabel: string; + @Input('aria-label') ariaLabel!: string; /** Input that can be used to specify the `aria-labelledby` attribute. */ - @Input('aria-labelledby') ariaLabelledby: string; + @Input('aria-labelledby') ariaLabelledby!: string; /** Function that maps an option's control value to its display value in the trigger. */ @Input() displayWith: ((value: any) => string) | null = null; @@ -187,10 +187,10 @@ export class MatAutocomplete implements AfterContentInit, OnDestroy { * Specify the width of the autocomplete panel. Can be any CSS sizing value, otherwise it will * match the width of its host. */ - @Input() panelWidth: string | number; + @Input() panelWidth!: string | number; /** Whether ripples are disabled within the autocomplete panel. */ - @Input({transform: booleanAttribute}) disableRipple: boolean; + @Input({transform: booleanAttribute}) disableRipple: boolean = false; /** Event that is emitted whenever an option from the list is selected. */ @Output() readonly optionSelected: EventEmitter = @@ -215,7 +215,7 @@ export class MatAutocomplete implements AfterContentInit, OnDestroy { this._classList = value; this._elementRef.nativeElement.className = ''; } - _classList: string | string[]; + _classList!: string | string[]; /** Whether checkmark indicator for single-selection options is hidden. */ @Input({transform: booleanAttribute}) diff --git a/src/material/autocomplete/autocomplete.zone.spec.ts b/src/material/autocomplete/autocomplete.zone.spec.ts index 19f49ff6ff0c..c7869029f113 100644 --- a/src/material/autocomplete/autocomplete.zone.spec.ts +++ b/src/material/autocomplete/autocomplete.zone.spec.ts @@ -119,26 +119,26 @@ describe('MatAutocomplete Zone.js integration', () => { }) class SimpleAutocomplete implements OnDestroy { stateCtrl = new FormControl<{name: string; code: string} | string | null>(null); - filteredStates: any[]; - valueSub: Subscription; + filteredStates!: any[]; + valueSub!: Subscription; floatLabel: FloatLabelType = 'auto'; position: 'auto' | 'above' | 'below' = 'auto'; - width: number; + width!: number; disableRipple = false; autocompleteDisabled = false; hasLabel = true; requireSelection = false; - ariaLabel: string; - ariaLabelledby: string; + ariaLabel!: string; + ariaLabelledby!: string; panelClass = 'class-one class-two'; - theme: ThemePalette; + theme!: ThemePalette; openedSpy = jasmine.createSpy('autocomplete opened spy'); closedSpy = jasmine.createSpy('autocomplete closed spy'); - @ViewChild(MatAutocompleteTrigger, {static: true}) trigger: MatAutocompleteTrigger; - @ViewChild(MatAutocomplete) panel: MatAutocomplete; - @ViewChild(MatFormField) formField: MatFormField; - @ViewChildren(MatOption) options: QueryList; + @ViewChild(MatAutocompleteTrigger, {static: true}) trigger!: MatAutocompleteTrigger; + @ViewChild(MatAutocomplete) panel!: MatAutocomplete; + @ViewChild(MatFormField) formField!: MatFormField; + @ViewChildren(MatOption) options!: QueryList; states: {code: string; name: string; height?: number; disabled?: boolean}[] = [ {code: 'AL', name: 'Alabama'}, From 8a3278fb0108a85a313a18ee9ee8893dbd69d531 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 10:59:11 +0200 Subject: [PATCH 03/35] refactor(material/badge): fix strict property initialization errors Updates the code to be compatible with strict property initialization. --- src/material/badge/badge.spec.ts | 2 +- src/material/badge/badge.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/material/badge/badge.spec.ts b/src/material/badge/badge.spec.ts index 47d3b899976a..4ef104a1f41d 100644 --- a/src/material/badge/badge.spec.ts +++ b/src/material/badge/badge.spec.ts @@ -280,7 +280,7 @@ describe('MatBadge', () => { imports: [MatBadgeModule], }) class BadgeOnInteractiveElement { - @ViewChild(MatBadge) badgeInstance: MatBadge; + @ViewChild(MatBadge) badgeInstance!: MatBadge; badgeColor = signal(undefined); badgeContent = signal('1'); badgeDirection = signal('above after'); diff --git a/src/material/badge/badge.ts b/src/material/badge/badge.ts index 2a1745f13560..f87d7546153e 100644 --- a/src/material/badge/badge.ts +++ b/src/material/badge/badge.ts @@ -101,7 +101,7 @@ export class MatBadge implements OnInit, OnDestroy { @Input({alias: 'matBadgeOverlap', transform: booleanAttribute}) overlap: boolean = true; /** Whether the badge is disabled. */ - @Input({alias: 'matBadgeDisabled', transform: booleanAttribute}) disabled: boolean; + @Input({alias: 'matBadgeDisabled', transform: booleanAttribute}) disabled: boolean = false; /** * Position the badge should reside. @@ -127,13 +127,13 @@ export class MatBadge implements OnInit, OnDestroy { set description(newDescription: string) { this._updateDescription(newDescription); } - private _description: string; + private _description!: string; /** Size of the badge. Can be 'small', 'medium', or 'large'. */ @Input('matBadgeSize') size: MatBadgeSize = 'medium'; /** Whether the badge is hidden. */ - @Input({alias: 'matBadgeHidden', transform: booleanAttribute}) hidden: boolean; + @Input({alias: 'matBadgeHidden', transform: booleanAttribute}) hidden: boolean = false; /** Visible badge element. */ private _badgeElement: HTMLElement | undefined; From 7213fa599ca484c15b0b3af0ea18fe0116af1086 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 10:59:50 +0200 Subject: [PATCH 04/35] refactor(material/bottom-sheet): fix strict property initialization errors Updates the code to be compatible with strict property initialization. --- src/material/bottom-sheet/bottom-sheet-container.ts | 2 +- src/material/bottom-sheet/bottom-sheet-ref.ts | 2 +- src/material/bottom-sheet/bottom-sheet.spec.ts | 8 ++++---- .../bottom-sheet/testing/bottom-sheet-harness.spec.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/material/bottom-sheet/bottom-sheet-container.ts b/src/material/bottom-sheet/bottom-sheet-container.ts index 66dddd36b02c..82c66aca9021 100644 --- a/src/material/bottom-sheet/bottom-sheet-container.ts +++ b/src/material/bottom-sheet/bottom-sheet-container.ts @@ -66,7 +66,7 @@ export class MatBottomSheetContainer extends CdkDialogContainer implements OnDes }>(); /** Whether the component has been destroyed. */ - private _destroyed: boolean; + private _destroyed = false; constructor(...args: unknown[]); diff --git a/src/material/bottom-sheet/bottom-sheet-ref.ts b/src/material/bottom-sheet/bottom-sheet-ref.ts index d52e20e18428..2e005b471d56 100644 --- a/src/material/bottom-sheet/bottom-sheet-ref.ts +++ b/src/material/bottom-sheet/bottom-sheet-ref.ts @@ -47,7 +47,7 @@ export class MatBottomSheetRef { private _result: R | undefined; /** Handle to the timeout that's running as a fallback in case the exit animation doesn't fire. */ - private _closeFallbackTimeout: ReturnType; + private _closeFallbackTimeout: ReturnType | undefined; constructor( private _ref: DialogRef, diff --git a/src/material/bottom-sheet/bottom-sheet.spec.ts b/src/material/bottom-sheet/bottom-sheet.spec.ts index e17deec2a6f2..0893cbfe41a9 100644 --- a/src/material/bottom-sheet/bottom-sheet.spec.ts +++ b/src/material/bottom-sheet/bottom-sheet.spec.ts @@ -1022,7 +1022,7 @@ class DirectiveWithViewContainer { imports: [DirectiveWithViewContainer], }) class ComponentWithChildViewContainer { - @ViewChild(DirectiveWithViewContainer) childWithViewContainer: DirectiveWithViewContainer; + @ViewChild(DirectiveWithViewContainer) childWithViewContainer!: DirectiveWithViewContainer; get childViewContainer() { return this.childWithViewContainer.viewContainerRef; @@ -1035,10 +1035,10 @@ class ComponentWithChildViewContainer { Cheese {{localValue}} {{data?.value}}{{setRef(bottomSheetRef)}}`, }) class ComponentWithTemplateRef { - localValue: string; - bottomSheetRef: MatBottomSheetRef; + localValue!: string; + bottomSheetRef!: MatBottomSheetRef; - @ViewChild(TemplateRef) templateRef: TemplateRef; + @ViewChild(TemplateRef) templateRef!: TemplateRef; setRef(bottomSheetRef: MatBottomSheetRef): string { this.bottomSheetRef = bottomSheetRef; diff --git a/src/material/bottom-sheet/testing/bottom-sheet-harness.spec.ts b/src/material/bottom-sheet/testing/bottom-sheet-harness.spec.ts index 19357c6355d5..7decabddf960 100644 --- a/src/material/bottom-sheet/testing/bottom-sheet-harness.spec.ts +++ b/src/material/bottom-sheet/testing/bottom-sheet-harness.spec.ts @@ -50,7 +50,7 @@ describe('MatBottomSheetHarness', () => { class BottomSheetHarnessTest { readonly bottomSheet = inject(MatBottomSheet); - @ViewChild(TemplateRef) template: TemplateRef; + @ViewChild(TemplateRef) template!: TemplateRef; open(config?: MatBottomSheetConfig) { return this.bottomSheet.open(this.template, config); From f41e3ffc2e16f464e9b4b03816594343472de0fb Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 11:00:26 +0200 Subject: [PATCH 05/35] refactor(material/button): fix strict property initialization errors Updates the code to be compatible with strict property initialization. --- src/material/button/button-base.ts | 2 +- src/material/button/button.spec.ts | 4 ++-- src/material/button/fab.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/material/button/button-base.ts b/src/material/button/button-base.ts index 1c048f6bb456..c4e6957ef140 100644 --- a/src/material/button/button-base.ts +++ b/src/material/button/button-base.ts @@ -139,7 +139,7 @@ export class MatButtonBase implements AfterViewInit, OnDestroy { /** Tab index for the button. */ @Input({transform: transformTabIndex}) - tabIndex: number; + tabIndex!: number; /** * Backwards-compatibility input that handles pre-existing `[tabindex]` bindings. diff --git a/src/material/button/button.spec.ts b/src/material/button/button.spec.ts index 1f7233b9bf0e..61d7addb5e1d 100644 --- a/src/material/button/button.spec.ts +++ b/src/material/button/button.spec.ts @@ -464,8 +464,8 @@ class TestApp { clickCount = 0; isDisabled = false; rippleDisabled = false; - buttonColor: ThemePalette; - tabIndex: number; + buttonColor!: ThemePalette; + tabIndex!: number; extended = false; disabledInteractive = false; appearance: MatButtonAppearance = 'text'; diff --git a/src/material/button/fab.ts b/src/material/button/fab.ts index 9822e922f9be..bf63ceac6652 100644 --- a/src/material/button/fab.ts +++ b/src/material/button/fab.ts @@ -71,7 +71,7 @@ export class MatFabButton extends MatButtonBase { override _isFab = true; - @Input({transform: booleanAttribute}) extended: boolean; + @Input({transform: booleanAttribute}) extended: boolean = false; constructor(...args: unknown[]); From 92bd6931590f0230fc26f626edd79d9c666bd3fe Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 11:02:38 +0200 Subject: [PATCH 06/35] refactor(material/button-toggle): fix strict property initialization errors Updates the code to be compatible with strict property initialization. --- .../button-toggle/button-toggle.spec.ts | 20 ++++++++-------- src/material/button-toggle/button-toggle.ts | 24 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/material/button-toggle/button-toggle.spec.ts b/src/material/button-toggle/button-toggle.spec.ts index 6a1177dcee73..e213f4eadaae 100644 --- a/src/material/button-toggle/button-toggle.spec.ts +++ b/src/material/button-toggle/button-toggle.spec.ts @@ -1171,7 +1171,7 @@ class ButtonTogglesInsideButtonToggleGroup { isGroupDisabled: boolean = false; disabledIntearctive = false; isVertical: boolean = false; - groupValue: string; + groupValue!: string; renderFirstToggle = true; } @@ -1193,13 +1193,13 @@ class ButtonTogglesInsideButtonToggleGroup { }) class ButtonToggleGroupWithNgModel { groupName = 'group-name'; - modelValue: string; + modelValue!: string; options = [ {label: 'Red', value: 'red', name: ''}, {label: 'Green', value: 'green', name: ''}, {label: 'Blue', value: 'blue', name: ''}, ]; - lastEvent: MatButtonToggleChange; + lastEvent!: MatButtonToggleChange; disableRipple = false; } @@ -1231,7 +1231,7 @@ class ButtonTogglesInsideButtonToggleGroupMultiple { }) class FalsyButtonTogglesInsideButtonToggleGroupMultiple { value: ('' | number | null | undefined | boolean)[] = [0]; - @ViewChildren(MatButtonToggle) toggles: QueryList; + @ViewChildren(MatButtonToggle) toggles!: QueryList; } @Component({ @@ -1252,7 +1252,7 @@ class StandaloneButtonToggle {} imports: [MatButtonToggleModule], }) class ButtonToggleGroupWithInitialValue { - lastEvent: MatButtonToggleChange; + lastEvent!: MatButtonToggleChange; } @Component({ @@ -1311,8 +1311,8 @@ class ButtonToggleWithAriaLabelledby {} imports: [MatButtonToggleModule], }) class RepeatedButtonTogglesWithPreselectedValue { - @ViewChild(MatButtonToggleGroup) toggleGroup: MatButtonToggleGroup; - @ViewChildren(MatButtonToggle) toggles: QueryList; + @ViewChild(MatButtonToggleGroup) toggleGroup!: MatButtonToggleGroup; + @ViewChildren(MatButtonToggle) toggles!: QueryList; possibleValues: (string | null)[] = ['One', 'Two', 'Three']; value: string | null = 'Two'; @@ -1340,8 +1340,8 @@ class ButtonToggleWithStaticName {} imports: [MatButtonToggleModule], }) class ButtonToggleWithStaticChecked { - @ViewChild(MatButtonToggleGroup) group: MatButtonToggleGroup; - @ViewChildren(MatButtonToggle) toggles: QueryList; + @ViewChild(MatButtonToggleGroup) group!: MatButtonToggleGroup; + @ViewChildren(MatButtonToggle) toggles!: QueryList; } @Component({ @@ -1363,7 +1363,7 @@ class ButtonToggleWithStaticAriaAttributes {} imports: [MatButtonToggleModule, FormsModule, ReactiveFormsModule], }) class ButtonToggleGroupWithFormControlAndDynamicButtons { - @ViewChildren(MatButtonToggle) toggles: QueryList; + @ViewChildren(MatButtonToggle) toggles!: QueryList; control = new FormControl(''); values = ['a', 'b', 'c']; } diff --git a/src/material/button-toggle/button-toggle.ts b/src/material/button-toggle/button-toggle.ts index d5948dfc3b07..0b1da176f916 100644 --- a/src/material/button-toggle/button-toggle.ts +++ b/src/material/button-toggle/button-toggle.ts @@ -145,7 +145,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After private _multiple = false; private _disabled = false; private _disabledInteractive = false; - private _selectionModel: SelectionModel; + private _selectionModel!: SelectionModel; /** * Reference to the raw value that the consumer tried to assign. The real @@ -170,7 +170,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After // from nested groups, but that's not a case that we support. descendants: true, }) - _buttonToggles: QueryList; + _buttonToggles!: QueryList; /** The appearance for all the buttons in the group. */ @Input() appearance: MatButtonToggleAppearance; @@ -187,7 +187,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After private _name = inject(_IdGenerator).getId('mat-button-toggle-group-'); /** Whether the toggle group is vertical. */ - @Input({transform: booleanAttribute}) vertical: boolean; + @Input({transform: booleanAttribute}) vertical: boolean = false; /** Value of the toggle group. */ @Input() @@ -289,8 +289,8 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After this.appearance = defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard'; - this.hideSingleSelectionIndicator = defaultOptions?.hideSingleSelectionIndicator ?? false; - this.hideMultipleSelectionIndicator = defaultOptions?.hideMultipleSelectionIndicator ?? false; + this._hideSingleSelectionIndicator = defaultOptions?.hideSingleSelectionIndicator ?? false; + this._hideMultipleSelectionIndicator = defaultOptions?.hideMultipleSelectionIndicator ?? false; } ngOnInit() { @@ -583,7 +583,7 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { * Attached to the aria-label attribute of the host element. In most cases, aria-labelledby will * take precedence so this may be omitted. */ - @Input('aria-label') ariaLabel: string; + @Input('aria-label') ariaLabel!: string; /** * Users can specify the `aria-labelledby` attribute which will be forwarded to the input element @@ -591,7 +591,7 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { @Input('aria-labelledby') ariaLabelledby: string | null = null; /** Underlying native `button` element. */ - @ViewChild('button') _buttonElement: ElementRef; + @ViewChild('button') _buttonElement!: ElementRef; /** The parent button toggle group (exclusive selection). Optional. */ buttonToggleGroup: MatButtonToggleGroup; @@ -602,10 +602,10 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { } /** The unique ID for this button toggle. */ - @Input() id: string; + @Input() id!: string; /** HTML's 'name' attribute used to group radios for unique selection. */ - @Input() name: string; + @Input() name!: string; /** MatButtonToggleGroup reads this to assign its own value. */ @Input() value: any; @@ -621,7 +621,7 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { private _tabIndex: WritableSignal; /** Whether ripples are disabled on the button toggle. */ - @Input({transform: booleanAttribute}) disableRipple: boolean; + @Input({transform: booleanAttribute}) disableRipple: boolean = false; /** The appearance style of the button. */ @Input() @@ -690,9 +690,9 @@ export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy { this._tabIndex = signal(parseInt(defaultTabIndex) || 0); this.buttonToggleGroup = toggleGroup; - this.appearance = + this._appearance = defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard'; - this.disabledInteractive = defaultOptions?.disabledInteractive ?? false; + this._disabledInteractive = defaultOptions?.disabledInteractive ?? false; } ngOnInit() { From bb215e6c4cdb3d637ef88cef199c799f4b27a0d3 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 16 Dec 2025 11:04:01 +0200 Subject: [PATCH 07/35] refactor(material/checkbox): fix strict property initialization errors Updates the code to be compatible with strict property initialization. --- src/material/checkbox/checkbox.spec.ts | 6 +++--- src/material/checkbox/checkbox.ts | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/material/checkbox/checkbox.spec.ts b/src/material/checkbox/checkbox.spec.ts index ad2336d8c585..e4b3b070b669 100644 --- a/src/material/checkbox/checkbox.spec.ts +++ b/src/material/checkbox/checkbox.spec.ts @@ -718,7 +718,7 @@ describe('MatCheckbox', () => { // We're checking the arguments type / emitted value to be a boolean, because sometimes the // emitted value can be a DOM Event, which is not valid. // See angular/angular#4059 - expect(testComponent.lastEvent.checked).toBe(true); + expect(testComponent.lastEvent?.checked).toBe(true); })); }); @@ -1346,7 +1346,7 @@ class CheckboxWithNameAttribute {} imports: [MatCheckbox], }) class CheckboxWithChangeEvent { - lastEvent: MatCheckboxChange; + lastEvent: MatCheckboxChange | undefined; } /** Test component with reactive forms */ @@ -1364,7 +1364,7 @@ class CheckboxWithFormControl { imports: [MatCheckbox], }) class CheckboxWithoutLabel { - label: string; + label = ''; } /** Test component with the native tabindex attribute. */ diff --git a/src/material/checkbox/checkbox.ts b/src/material/checkbox/checkbox.ts index fbfd984eb534..41965b97641f 100644 --- a/src/material/checkbox/checkbox.ts +++ b/src/material/checkbox/checkbox.ts @@ -67,9 +67,9 @@ export enum TransitionCheckState { /** Change event object emitted by checkbox. */ export class MatCheckboxChange { /** The source checkbox of the event. */ - source: MatCheckbox; + source!: MatCheckbox; /** The new `checked` value of the checkbox. */ - checked: boolean; + checked!: boolean; } @Component({ @@ -158,20 +158,20 @@ export class MatCheckbox @Input('aria-labelledby') ariaLabelledby: string | null = null; /** The 'aria-describedby' attribute is read after the element's label and field type. */ - @Input('aria-describedby') ariaDescribedby: string; + @Input('aria-describedby') ariaDescribedby!: string; /** * Users can specify the `aria-expanded` attribute which will be forwarded to the input element */ - @Input({alias: 'aria-expanded', transform: booleanAttribute}) ariaExpanded: boolean; + @Input({alias: 'aria-expanded', transform: booleanAttribute}) ariaExpanded!: boolean; /** * Users can specify the `aria-controls` attribute which will be forwarded to the input element */ - @Input('aria-controls') ariaControls: string; + @Input('aria-controls') ariaControls!: string; /** Users can specify the `aria-owns` attribute which will be forwarded to the input element */ - @Input('aria-owns') ariaOwns: string; + @Input('aria-owns') ariaOwns!: string; private _uniqueId: string; @@ -184,7 +184,7 @@ export class MatCheckbox } /** Whether the checkbox is required. */ - @Input({transform: booleanAttribute}) required: boolean; + @Input({transform: booleanAttribute}) required: boolean = false; /** Whether the label should appear after or before the checkbox. Defaults to 'after' */ @Input() labelPosition: 'before' | 'after' = 'after'; @@ -199,16 +199,16 @@ export class MatCheckbox @Output() readonly indeterminateChange: EventEmitter = new EventEmitter(); /** The value attribute of the native input element */ - @Input() value: string; + @Input() value!: string; /** Whether the checkbox has a ripple. */ - @Input({transform: booleanAttribute}) disableRipple: boolean; + @Input({transform: booleanAttribute}) disableRipple: boolean = false; /** The native `` element */ - @ViewChild('input') _inputElement: ElementRef; + @ViewChild('input') _inputElement!: ElementRef; /** The native `