Skip to content

Commit

Permalink
feat: add support for strict null checks (#5094)
Browse files Browse the repository at this point in the history
  • Loading branch information
crisbeto authored and jelbourn committed Jun 21, 2017
1 parent 49dfe60 commit 2bf7024
Show file tree
Hide file tree
Showing 122 changed files with 764 additions and 662 deletions.
4 changes: 2 additions & 2 deletions e2e/components/tabs-e2e.spec.ts
Expand Up @@ -75,7 +75,7 @@ describe('tabs', () => {
*/
async function getFocusStates(elements: ElementArrayFinder) {
return elements.map(async (element) => {
let elementText = await element.getText();
let elementText = await element!.getText();
let activeText = await browser.driver.switchTo().activeElement().getText();

return activeText === elementText;
Expand All @@ -98,7 +98,7 @@ function getBodyActiveStates(elements: ElementArrayFinder) {
*/
async function getClassStates(elements: ElementArrayFinder, className: string) {
return elements.map(async (element) => {
let classes = await element.getAttribute('class');
let classes = await element!.getAttribute('class');
return classes.split(/ +/g).indexOf(className) >= 0;
});
}
1 change: 1 addition & 0 deletions e2e/tsconfig.json
Expand Up @@ -5,6 +5,7 @@
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"inlineSources": true,
"lib": ["es2015"],
"module": "commonjs",
Expand Down
1 change: 1 addition & 0 deletions src/cdk/tsconfig-build.json
Expand Up @@ -7,6 +7,7 @@
"stripInternal": false,
"experimentalDecorators": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"importHelpers": true,
"newLine": "lf",
"module": "es2015",
Expand Down
5 changes: 5 additions & 0 deletions src/demo-app/checkbox/checkbox-demo.ts
Expand Up @@ -41,6 +41,11 @@ export class MdCheckboxDemoNestedChecklist {

allComplete(task: Task): boolean {
let subtasks = task.subtasks;

if (!subtasks) {
return false;
}

return subtasks.every(t => t.completed) ? true
: subtasks.every(t => !t.completed) ? false
: task.completed;
Expand Down
4 changes: 2 additions & 2 deletions src/demo-app/data-table/data-table-demo.ts
Expand Up @@ -2,7 +2,7 @@ import {Component} from '@angular/core';
import {PeopleDatabase} from './people-database';
import {PersonDataSource} from './person-data-source';

export type UserProperties = 'userId' | 'userName' | 'progress' | 'color';
export type UserProperties = 'userId' | 'userName' | 'progress' | 'color' | undefined;

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

constructor(private _peopleDatabase: PeopleDatabase) {
Expand Down
6 changes: 3 additions & 3 deletions src/demo-app/dialog/dialog-demo.ts
@@ -1,6 +1,6 @@
import {Component, Inject, ViewChild, TemplateRef} from '@angular/core';
import {DOCUMENT} from '@angular/platform-browser';
import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/material';
import {MdDialog, MdDialogRef, MD_DIALOG_DATA} from '@angular/material';


@Component({
Expand All @@ -10,10 +10,10 @@ import {MdDialog, MdDialogRef, MdDialogConfig, MD_DIALOG_DATA} from '@angular/ma
styleUrls: ['dialog-demo.css'],
})
export class DialogDemo {
dialogRef: MdDialogRef<JazzDialog>;
dialogRef: MdDialogRef<JazzDialog> | null;
lastCloseResult: string;
actionsAlignment: string;
config: MdDialogConfig = {
config = {
disableClose: false,
panelClass: 'custom-overlay-pane-class',
hasBackdrop: true,
Expand Down
2 changes: 1 addition & 1 deletion src/demo-app/ripple/ripple-demo.ts
Expand Up @@ -15,7 +15,7 @@ export class RippleDemo {
disabled = false;
unbounded = false;
rounded = false;
radius: number = null;
radius: number;
rippleSpeed = 1;
rippleColor = '';

Expand Down
4 changes: 2 additions & 2 deletions src/demo-app/snack-bar/snack-bar-demo.ts
Expand Up @@ -21,7 +21,7 @@ export class SnackBarDemo {
open() {
let config = new MdSnackBarConfig();
config.duration = this.autoHide;
config.extraClasses = this.addExtraClass ? ['party'] : null;
this.snackBar.open(this.message, this.action && this.actionButtonLabel, config);
config.extraClasses = this.addExtraClass ? ['party'] : undefined;
this.snackBar.open(this.message, this.action ? this.actionButtonLabel : undefined, config);
}
}
3 changes: 2 additions & 1 deletion src/demo-app/tsconfig-aot.json
@@ -1,4 +1,4 @@
// TypeScript config that extends the demo-app tsconfig file. This config compiles the
// TypeScript config that extends the demo-app tsconfig file. This config compiles the
// "main-aot.ts" file and also enables templage code generation / AOT. All paths need
// to be relative to the output directory.
{
Expand All @@ -7,6 +7,7 @@
"experimentalDecorators": true,
// TODO(paul): Remove once Angular has been upgraded and supports noUnusedParameters in AOT.
"noUnusedParameters": false,
"strictNullChecks": true,
"outDir": ".",
"paths": {
"@angular/material": ["./material"],
Expand Down
1 change: 1 addition & 0 deletions src/demo-app/tsconfig-build.json
Expand Up @@ -6,6 +6,7 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"lib": ["es6", "es2015", "dom"],
"module": "commonjs",
"moduleResolution": "node",
Expand Down
7 changes: 2 additions & 5 deletions src/e2e-app/dialog/dialog-e2e.ts
Expand Up @@ -7,18 +7,15 @@ import {MdDialog, MdDialogRef, MdDialogConfig} from '@angular/material';
templateUrl: 'dialog-e2e.html'
})
export class DialogE2E {
dialogRef: MdDialogRef<TestDialog>;
dialogRef: MdDialogRef<TestDialog> | null;

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

constructor (private _dialog: MdDialog) { }

private _openDialog(config?: MdDialogConfig) {
this.dialogRef = this._dialog.open(TestDialog, config);

this.dialogRef.afterClosed().subscribe(() => {
this.dialogRef = null;
});
this.dialogRef.afterClosed().subscribe(() => this.dialogRef = null);
}

openDefault() {
Expand Down
2 changes: 1 addition & 1 deletion src/e2e-app/fullscreen/fullscreen-e2e.ts
Expand Up @@ -8,7 +8,7 @@ import {MdDialog, MdDialogRef} from '@angular/material';
})
export class FullscreenE2E {

dialogRef: MdDialogRef<TestDialog>;
dialogRef: MdDialogRef<TestDialog> | null;

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

Expand Down
1 change: 1 addition & 0 deletions src/e2e-app/tsconfig-build.json
Expand Up @@ -7,6 +7,7 @@
"experimentalDecorators": true,
// TODO(paul): Remove once Angular has been upgraded and supports noUnusedParameters in AOT.
"noUnusedParameters": false,
"strictNullChecks": true,
"lib": ["es6", "es2015", "dom"],
"module": "commonjs",
"moduleResolution": "node",
Expand Down
21 changes: 13 additions & 8 deletions src/lib/autocomplete/autocomplete-trigger.ts
Expand Up @@ -35,6 +35,7 @@ import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/observable/of';

/**
* The following style constants are necessary to save here in order
Expand Down Expand Up @@ -86,7 +87,7 @@ export function getMdAutocompleteMissingPanelError(): Error {
providers: [MD_AUTOCOMPLETE_VALUE_ACCESSOR]
})
export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
private _overlayRef: OverlayRef;
private _overlayRef: OverlayRef | null;
private _portal: TemplatePortal;
private _panelOpen: boolean = false;

Expand Down Expand Up @@ -153,7 +154,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
this._overlayRef.updateSize();
}

if (!this._overlayRef.hasAttached()) {
if (this._overlayRef && !this._overlayRef.hasAttached()) {
this._overlayRef.attach(this._portal);
this._subscribeToClosingActions();
}
Expand Down Expand Up @@ -197,10 +198,12 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
}

/** The currently active option, coerced to MdOption type. */
get activeOption(): MdOption {
get activeOption(): MdOption | null {
if (this.autocomplete && this.autocomplete._keyManager) {
return this.autocomplete._keyManager.activeItem as MdOption;
}

return null;
}

/** Stream of clicks outside of the autocomplete panel. */
Expand All @@ -214,9 +217,11 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
return this._panelOpen &&
clickTarget !== this._element.nativeElement &&
(!inputContainer || !inputContainer.contains(clickTarget)) &&
!this._overlayRef.overlayElement.contains(clickTarget);
(!!this._overlayRef && !this._overlayRef.overlayElement.contains(clickTarget));
});
}

return Observable.of(null);
}

/**
Expand Down Expand Up @@ -312,8 +317,8 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
* height, so the active option will be just visible at the bottom of the panel.
*/
private _scrollToOption(): void {
const optionOffset =
this.autocomplete._keyManager.activeItemIndex * AUTOCOMPLETE_OPTION_HEIGHT;
const optionOffset = this.autocomplete._keyManager.activeItemIndex ?
this.autocomplete._keyManager.activeItemIndex * AUTOCOMPLETE_OPTION_HEIGHT : 0;
const newScrollTop =
Math.max(0, optionOffset - AUTOCOMPLETE_PANEL_HEIGHT + AUTOCOMPLETE_OPTION_HEIGHT);
this.autocomplete._setScrollTop(newScrollTop);
Expand Down Expand Up @@ -419,9 +424,9 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
return this._element.nativeElement.getBoundingClientRect().width;
}

/** Reset active item to null so arrow events will activate the correct options.*/
/** Reset active item to -1 so arrow events will activate the correct options.*/
private _resetActiveItem(): void {
this.autocomplete._keyManager.setActiveItem(null);
this.autocomplete._keyManager.setActiveItem(-1);
}

/**
Expand Down
24 changes: 12 additions & 12 deletions src/lib/autocomplete/autocomplete.spec.ts
Expand Up @@ -136,7 +136,7 @@ describe('MdAutocomplete', () => {
// Note that we're running outside the Angular zone, in order to be able
// to test properly without the subscription from `_subscribeToClosingActions`
// giving us a false positive.
fixture.ngZone.runOutsideAngular(() => {
fixture.ngZone!.runOutsideAngular(() => {
fixture.componentInstance.trigger.openPanel();

Promise.resolve().then(() => {
Expand Down Expand Up @@ -328,7 +328,7 @@ describe('MdAutocomplete', () => {
rtlFixture.componentInstance.trigger.openPanel();
rtlFixture.detectChanges();

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

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

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

const UP_ARROW_EVENT = createKeyboardEvent('keydown', UP_ARROW);
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
Expand Down Expand Up @@ -934,7 +934,7 @@ describe('MdAutocomplete', () => {
fixture.detectChanges();

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

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

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

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

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

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

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

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

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

widthFixture.componentInstance.trigger.closePanel();
widthFixture.detectChanges();
Expand All @@ -1194,7 +1194,7 @@ describe('MdAutocomplete', () => {
widthFixture.detectChanges();

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

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

expect(Math.ceil(parseFloat(overlayPane.style.width))).toBe(300);
expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(300);

widthFixture.componentInstance.width = 500;
widthFixture.detectChanges();
Expand All @@ -1218,7 +1218,7 @@ describe('MdAutocomplete', () => {
dispatchFakeEvent(input, 'input');
widthFixture.detectChanges();

expect(Math.ceil(parseFloat(overlayPane.style.width))).toBe(500);
expect(Math.ceil(parseFloat(overlayPane.style.width as string))).toBe(500);
});

it('should show the panel when the options are initialized later within a component with ' +
Expand Down
2 changes: 1 addition & 1 deletion src/lib/autocomplete/autocomplete.ts
Expand Up @@ -61,7 +61,7 @@ export class MdAutocomplete implements AfterContentInit {
@ContentChildren(MdOption) options: QueryList<MdOption>;

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

/** Unique ID to be used by autocomplete trigger's "aria-owns" property. */
id: string = `md-autocomplete-${_uniqueAutocompleteIdCounter++}`;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/button-toggle/button-toggle.html
Expand Up @@ -3,7 +3,7 @@
[type]="_type"
[id]="inputId"
[checked]="checked"
[disabled]="disabled"
[disabled]="disabled || null"
[name]="name"
(change)="_onInputChange($event)"
(click)="_onInputClick($event)">
Expand Down
4 changes: 2 additions & 2 deletions src/lib/button-toggle/button-toggle.spec.ts
Expand Up @@ -256,7 +256,7 @@ describe('MdButtonToggle', () => {
for (let buttonToggle of buttonToggleInstances) {
expect(buttonToggle.checked).toBe(groupInstance.value === buttonToggle.value);
}
expect(groupInstance.selected.value).toBe(groupInstance.value);
expect(groupInstance.selected!.value).toBe(groupInstance.value);
});

it('should have the correct FormControl state initially and after interaction',
Expand Down Expand Up @@ -595,7 +595,7 @@ describe('MdButtonToggle', () => {
class ButtonTogglesInsideButtonToggleGroup {
isGroupDisabled: boolean = false;
isVertical: boolean = false;
groupValue: string = null;
groupValue: string;
}

@Component({
Expand Down

0 comments on commit 2bf7024

Please sign in to comment.