Skip to content

Commit 2bf7024

Browse files
crisbetojelbourn
authored andcommitted
feat: add support for strict null checks (#5094)
1 parent 49dfe60 commit 2bf7024

File tree

122 files changed

+764
-662
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

122 files changed

+764
-662
lines changed

e2e/components/tabs-e2e.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('tabs', () => {
7575
*/
7676
async function getFocusStates(elements: ElementArrayFinder) {
7777
return elements.map(async (element) => {
78-
let elementText = await element.getText();
78+
let elementText = await element!.getText();
7979
let activeText = await browser.driver.switchTo().activeElement().getText();
8080

8181
return activeText === elementText;
@@ -98,7 +98,7 @@ function getBodyActiveStates(elements: ElementArrayFinder) {
9898
*/
9999
async function getClassStates(elements: ElementArrayFinder, className: string) {
100100
return elements.map(async (element) => {
101-
let classes = await element.getAttribute('class');
101+
let classes = await element!.getAttribute('class');
102102
return classes.split(/ +/g).indexOf(className) >= 0;
103103
});
104104
}

e2e/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"declaration": true,
66
"emitDecoratorMetadata": true,
77
"experimentalDecorators": true,
8+
"strictNullChecks": true,
89
"inlineSources": true,
910
"lib": ["es2015"],
1011
"module": "commonjs",

src/cdk/tsconfig-build.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"stripInternal": false,
88
"experimentalDecorators": true,
99
"noUnusedParameters": true,
10+
"strictNullChecks": true,
1011
"importHelpers": true,
1112
"newLine": "lf",
1213
"module": "es2015",

src/demo-app/checkbox/checkbox-demo.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ export class MdCheckboxDemoNestedChecklist {
4141

4242
allComplete(task: Task): boolean {
4343
let subtasks = task.subtasks;
44+
45+
if (!subtasks) {
46+
return false;
47+
}
48+
4449
return subtasks.every(t => t.completed) ? true
4550
: subtasks.every(t => !t.completed) ? false
4651
: task.completed;

src/demo-app/data-table/data-table-demo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {Component} from '@angular/core';
22
import {PeopleDatabase} from './people-database';
33
import {PersonDataSource} from './person-data-source';
44

5-
export type UserProperties = 'userId' | 'userName' | 'progress' | 'color';
5+
export type UserProperties = 'userId' | 'userName' | 'progress' | 'color' | undefined;
66

77
@Component({
88
moduleId: module.id,
@@ -11,7 +11,7 @@ export type UserProperties = 'userId' | 'userName' | 'progress' | 'color';
1111
styleUrls: ['data-table-demo.css'],
1212
})
1313
export class DataTableDemo {
14-
dataSource: PersonDataSource;
14+
dataSource: PersonDataSource | null;
1515
propertiesToDisplay: UserProperties[] = [];
1616

1717
constructor(private _peopleDatabase: PeopleDatabase) {

src/demo-app/dialog/dialog-demo.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Component, Inject, ViewChild, TemplateRef} from '@angular/core';
22
import {DOCUMENT} from '@angular/platform-browser';
3-
import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/material';
3+
import {MdDialog, MdDialogRef, MD_DIALOG_DATA} from '@angular/material';
44

55

66
@Component({
@@ -10,10 +10,10 @@ import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/ma
1010
styleUrls: ['dialog-demo.css'],
1111
})
1212
export class DialogDemo {
13-
dialogRef: MdDialogRef<JazzDialog>;
13+
dialogRef: MdDialogRef<JazzDialog> | null;
1414
lastCloseResult: string;
1515
actionsAlignment: string;
16-
config: MdDialogConfig = {
16+
config = {
1717
disableClose: false,
1818
panelClass: 'custom-overlay-pane-class',
1919
hasBackdrop: true,

src/demo-app/ripple/ripple-demo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class RippleDemo {
1515
disabled = false;
1616
unbounded = false;
1717
rounded = false;
18-
radius: number = null;
18+
radius: number;
1919
rippleSpeed = 1;
2020
rippleColor = '';
2121

src/demo-app/snack-bar/snack-bar-demo.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class SnackBarDemo {
2121
open() {
2222
let config = new MdSnackBarConfig();
2323
config.duration = this.autoHide;
24-
config.extraClasses = this.addExtraClass ? ['party'] : null;
25-
this.snackBar.open(this.message, this.action && this.actionButtonLabel, config);
24+
config.extraClasses = this.addExtraClass ? ['party'] : undefined;
25+
this.snackBar.open(this.message, this.action ? this.actionButtonLabel : undefined, config);
2626
}
2727
}

src/demo-app/tsconfig-aot.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// TypeScript config that extends the demo-app tsconfig file. This config compiles the
1+
// TypeScript config that extends the demo-app tsconfig file. This config compiles the
22
// "main-aot.ts" file and also enables templage code generation / AOT. All paths need
33
// to be relative to the output directory.
44
{
@@ -7,6 +7,7 @@
77
"experimentalDecorators": true,
88
// TODO(paul): Remove once Angular has been upgraded and supports noUnusedParameters in AOT.
99
"noUnusedParameters": false,
10+
"strictNullChecks": true,
1011
"outDir": ".",
1112
"paths": {
1213
"@angular/material": ["./material"],

src/demo-app/tsconfig-build.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"emitDecoratorMetadata": true,
77
"experimentalDecorators": true,
88
"noUnusedParameters": true,
9+
"strictNullChecks": true,
910
"lib": ["es6", "es2015", "dom"],
1011
"module": "commonjs",
1112
"moduleResolution": "node",

src/e2e-app/dialog/dialog-e2e.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,15 @@ import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';
77
templateUrl: 'dialog-e2e.html'
88
})
99
export class DialogE2E {
10-
dialogRef: MdDialogRef<TestDialog>;
10+
dialogRef: MdDialogRef<TestDialog> | null;
1111

1212
@ViewChild(TemplateRef) templateRef: TemplateRef<any>;
1313

1414
constructor (private _dialog: MdDialog) { }
1515

1616
private _openDialog(config?: MdDialogConfig) {
1717
this.dialogRef = this._dialog.open(TestDialog, config);
18-
19-
this.dialogRef.afterClosed().subscribe(() => {
20-
this.dialogRef = null;
21-
});
18+
this.dialogRef.afterClosed().subscribe(() => this.dialogRef = null);
2219
}
2320

2421
openDefault() {

src/e2e-app/fullscreen/fullscreen-e2e.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {MdDialog, MdDialogRef} from '@angular/material';
88
})
99
export class FullscreenE2E {
1010

11-
dialogRef: MdDialogRef<TestDialog>;
11+
dialogRef: MdDialogRef<TestDialog> | null;
1212

1313
constructor (private _element: ElementRef, private _dialog: MdDialog) { }
1414

src/e2e-app/tsconfig-build.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"experimentalDecorators": true,
88
// TODO(paul): Remove once Angular has been upgraded and supports noUnusedParameters in AOT.
99
"noUnusedParameters": false,
10+
"strictNullChecks": true,
1011
"lib": ["es6", "es2015", "dom"],
1112
"module": "commonjs",
1213
"moduleResolution": "node",

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import 'rxjs/add/observable/merge';
3535
import 'rxjs/add/observable/fromEvent';
3636
import 'rxjs/add/operator/filter';
3737
import 'rxjs/add/operator/switchMap';
38+
import 'rxjs/add/observable/of';
3839

3940
/**
4041
* The following style constants are necessary to save here in order
@@ -86,7 +87,7 @@ export function getMdAutocompleteMissingPanelError(): Error {
8687
providers: [MD_AUTOCOMPLETE_VALUE_ACCESSOR]
8788
})
8889
export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
89-
private _overlayRef: OverlayRef;
90+
private _overlayRef: OverlayRef | null;
9091
private _portal: TemplatePortal;
9192
private _panelOpen: boolean = false;
9293

@@ -153,7 +154,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
153154
this._overlayRef.updateSize();
154155
}
155156

156-
if (!this._overlayRef.hasAttached()) {
157+
if (this._overlayRef && !this._overlayRef.hasAttached()) {
157158
this._overlayRef.attach(this._portal);
158159
this._subscribeToClosingActions();
159160
}
@@ -197,10 +198,12 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
197198
}
198199

199200
/** The currently active option, coerced to MdOption type. */
200-
get activeOption(): MdOption {
201+
get activeOption(): MdOption | null {
201202
if (this.autocomplete && this.autocomplete._keyManager) {
202203
return this.autocomplete._keyManager.activeItem as MdOption;
203204
}
205+
206+
return null;
204207
}
205208

206209
/** Stream of clicks outside of the autocomplete panel. */
@@ -214,9 +217,11 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
214217
return this._panelOpen &&
215218
clickTarget !== this._element.nativeElement &&
216219
(!inputContainer || !inputContainer.contains(clickTarget)) &&
217-
!this._overlayRef.overlayElement.contains(clickTarget);
220+
(!!this._overlayRef && !this._overlayRef.overlayElement.contains(clickTarget));
218221
});
219222
}
223+
224+
return Observable.of(null);
220225
}
221226

222227
/**
@@ -312,8 +317,8 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
312317
* height, so the active option will be just visible at the bottom of the panel.
313318
*/
314319
private _scrollToOption(): void {
315-
const optionOffset =
316-
this.autocomplete._keyManager.activeItemIndex * AUTOCOMPLETE_OPTION_HEIGHT;
320+
const optionOffset = this.autocomplete._keyManager.activeItemIndex ?
321+
this.autocomplete._keyManager.activeItemIndex * AUTOCOMPLETE_OPTION_HEIGHT : 0;
317322
const newScrollTop =
318323
Math.max(0, optionOffset - AUTOCOMPLETE_PANEL_HEIGHT + AUTOCOMPLETE_OPTION_HEIGHT);
319324
this.autocomplete._setScrollTop(newScrollTop);
@@ -419,9 +424,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
419424
return this._element.nativeElement.getBoundingClientRect().width;
420425
}
421426

422-
/** Reset active item to null so arrow events will activate the correct options.*/
427+
/** Reset active item to -1 so arrow events will activate the correct options.*/
423428
private _resetActiveItem(): void {
424-
this.autocomplete._keyManager.setActiveItem(null);
429+
this.autocomplete._keyManager.setActiveItem(-1);
425430
}
426431

427432
/**

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe('MdAutocomplete', () => {
136136
// Note that we're running outside the Angular zone, in order to be able
137137
// to test properly without the subscription from `_subscribeToClosingActions`
138138
// giving us a false positive.
139-
fixture.ngZone.runOutsideAngular(() => {
139+
fixture.ngZone!.runOutsideAngular(() => {
140140
fixture.componentInstance.trigger.openPanel();
141141

142142
Promise.resolve().then(() => {
@@ -328,7 +328,7 @@ describe('MdAutocomplete', () => {
328328
rtlFixture.componentInstance.trigger.openPanel();
329329
rtlFixture.detectChanges();
330330

331-
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane');
331+
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane')!;
332332
expect(overlayPane.getAttribute('dir')).toEqual('rtl');
333333

334334
});
@@ -731,7 +731,7 @@ describe('MdAutocomplete', () => {
731731
it('should scroll to active options below the fold', fakeAsync(() => {
732732
tick();
733733
const scrollContainer =
734-
document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel');
734+
document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel')!;
735735

736736
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
737737
tick();
@@ -752,7 +752,7 @@ describe('MdAutocomplete', () => {
752752
it('should scroll to active options on UP arrow', fakeAsync(() => {
753753
tick();
754754
const scrollContainer =
755-
document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel');
755+
document.querySelector('.cdk-overlay-pane .mat-autocomplete-panel')!;
756756

757757
const UP_ARROW_EVENT = createKeyboardEvent('keydown', UP_ARROW);
758758
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
@@ -934,7 +934,7 @@ describe('MdAutocomplete', () => {
934934
fixture.detectChanges();
935935

936936
const inputBottom = input.getBoundingClientRect().bottom;
937-
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel');
937+
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!;
938938
const panelTop = panel.getBoundingClientRect().top;
939939

940940
// Panel is offset by 6px in styles so that the underline has room to display.
@@ -958,7 +958,7 @@ describe('MdAutocomplete', () => {
958958
fixture.detectChanges();
959959

960960
const inputBottom = input.getBoundingClientRect().bottom;
961-
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel');
961+
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!;
962962
const panelTop = panel.getBoundingClientRect().top;
963963

964964
expect(Math.floor(inputBottom + 6)).toEqual(Math.floor(panelTop),
@@ -976,7 +976,7 @@ describe('MdAutocomplete', () => {
976976
fixture.detectChanges();
977977

978978
const inputTop = input.getBoundingClientRect().top;
979-
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel');
979+
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!;
980980
const panelBottom = panel.getBoundingClientRect().bottom;
981981

982982
// Panel is offset by 24px in styles so that the label has room to display.
@@ -999,7 +999,7 @@ describe('MdAutocomplete', () => {
999999
fixture.detectChanges();
10001000

10011001
const inputTop = input.getBoundingClientRect().top;
1002-
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel');
1002+
const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!;
10031003
const panelBottom = panel.getBoundingClientRect().bottom;
10041004

10051005
// Panel is offset by 24px in styles so that the label has room to display.
@@ -1182,7 +1182,7 @@ describe('MdAutocomplete', () => {
11821182

11831183
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
11841184
// Firefox, edge return a decimal value for width, so we need to parse and round it to verify
1185-
expect(Math.ceil(parseFloat(overlayPane.style.width))).toBe(300);
1185+
expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(300);
11861186

11871187
widthFixture.componentInstance.trigger.closePanel();
11881188
widthFixture.detectChanges();
@@ -1194,7 +1194,7 @@ describe('MdAutocomplete', () => {
11941194
widthFixture.detectChanges();
11951195

11961196
// Firefox, edge return a decimal value for width, so we need to parse and round it to verify
1197-
expect(Math.ceil(parseFloat(overlayPane.style.width))).toBe(500);
1197+
expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(500);
11981198
});
11991199

12001200
it('should update the width while the panel is open', () => {
@@ -1209,7 +1209,7 @@ describe('MdAutocomplete', () => {
12091209
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
12101210
const input = widthFixture.debugElement.query(By.css('input')).nativeElement;
12111211

1212-
expect(Math.ceil(parseFloat(overlayPane.style.width))).toBe(300);
1212+
expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(300);
12131213

12141214
widthFixture.componentInstance.width = 500;
12151215
widthFixture.detectChanges();
@@ -1218,7 +1218,7 @@ describe('MdAutocomplete', () => {
12181218
dispatchFakeEvent(input, 'input');
12191219
widthFixture.detectChanges();
12201220

1221-
expect(Math.ceil(parseFloat(overlayPane.style.width))).toBe(500);
1221+
expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(500);
12221222
});
12231223

12241224
it('should show the panel when the options are initialized later within a component with ' +

src/lib/autocomplete/autocomplete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class MdAutocomplete implements AfterContentInit {
6161
@ContentChildren(MdOption) options: QueryList<MdOption>;
6262

6363
/** Function that maps an option's control value to its display value in the trigger. */
64-
@Input() displayWith: (value: any) => string;
64+
@Input() displayWith: ((value: any) => string) | null = null;
6565

6666
/** Unique ID to be used by autocomplete trigger's "aria-owns" property. */
6767
id: string = `md-autocomplete-${_uniqueAutocompleteIdCounter++}`;

src/lib/button-toggle/button-toggle.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[type]="_type"
44
[id]="inputId"
55
[checked]="checked"
6-
[disabled]="disabled"
6+
[disabled]="disabled || null"
77
[name]="name"
88
(change)="_onInputChange($event)"
99
(click)="_onInputClick($event)">

src/lib/button-toggle/button-toggle.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ describe('MdButtonToggle', () => {
256256
for (let buttonToggle of buttonToggleInstances) {
257257
expect(buttonToggle.checked).toBe(groupInstance.value === buttonToggle.value);
258258
}
259-
expect(groupInstance.selected.value).toBe(groupInstance.value);
259+
expect(groupInstance.selected!.value).toBe(groupInstance.value);
260260
});
261261

262262
it('should have the correct FormControl state initially and after interaction',
@@ -595,7 +595,7 @@ describe('MdButtonToggle', () => {
595595
class ButtonTogglesInsideButtonToggleGroup {
596596
isGroupDisabled: boolean = false;
597597
isVertical: boolean = false;
598-
groupValue: string = null;
598+
groupValue: string;
599599
}
600600

601601
@Component({

0 commit comments

Comments
 (0)