style(aio): enforce strict TypeScript checks (#21342)

Closes #20646

PR Close #21342
This commit is contained in:
Pete Bacon Darwin 2018-01-10 10:41:15 +00:00 committed by Alex Eagle
parent 246de65140
commit c5c6d84fe6
57 changed files with 198 additions and 194 deletions

View File

@ -18,7 +18,6 @@
"@angular/core": "^5.0.0", "@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0", "@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0", "@angular/http": "^5.0.0",
"@angular/service-worker": "^5.0.0",
"@angular/platform-browser": "^5.0.0", "@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0", "@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0", "@angular/router": "^5.0.0",

View File

@ -25,7 +25,7 @@ export class ApiPage extends SitePage {
// and we want to be able to pull out the code elements from only the first level // and we want to be able to pull out the code elements from only the first level
// if `onlyDirect` is set to `true`. // if `onlyDirect` is set to `true`.
const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} li > :not(ul) code`; const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} li > :not(ul) code`;
return element.all(by.css(selector)).map<string>(item => item.getText()); return element.all(by.css(selector)).map<string>(item => item && item.getText());
} }
getOverview(docType) { getOverview(docType) {

View File

@ -62,6 +62,6 @@ export class SitePage {
getSearchResults() { getSearchResults() {
const results = element.all(by.css('.search-results li')); const results = element.all(by.css('.search-results li'));
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000); browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
return results.map(link => link.getText()); return results.map(link => link && link.getText());
} }
} }

View File

@ -314,7 +314,7 @@ describe('AppComponent', () => {
it('should not navigate when change to a version without a url', () => { it('should not navigate when change to a version without a url', () => {
setupSelectorForTesting(); setupSelectorForTesting();
const versionWithoutUrlIndex = component.docVersions.length; const versionWithoutUrlIndex = component.docVersions.length;
const versionWithoutUrl = component.docVersions[versionWithoutUrlIndex] = { title: 'foo', url: null }; const versionWithoutUrl = component.docVersions[versionWithoutUrlIndex] = { title: 'foo' };
selectElement.triggerEventHandler('change', { option: versionWithoutUrl, index: versionWithoutUrlIndex }); selectElement.triggerEventHandler('change', { option: versionWithoutUrl, index: versionWithoutUrlIndex });
expect(locationService.go).not.toHaveBeenCalled(); expect(locationService.go).not.toHaveBeenCalled();
}); });
@ -520,9 +520,9 @@ describe('AppComponent', () => {
describe('aio-toc', () => { describe('aio-toc', () => {
let tocDebugElement: DebugElement; let tocDebugElement: DebugElement;
let tocContainer: DebugElement; let tocContainer: DebugElement|null;
const setHasFloatingToc = hasFloatingToc => { const setHasFloatingToc = (hasFloatingToc: boolean) => {
component.hasFloatingToc = hasFloatingToc; component.hasFloatingToc = hasFloatingToc;
fixture.detectChanges(); fixture.detectChanges();
@ -551,12 +551,12 @@ describe('AppComponent', () => {
}); });
it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => { it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => {
expect(tocContainer.styles['max-height']).toBeNull(); expect(tocContainer!.styles['max-height']).toBeNull();
component.tocMaxHeight = '100'; component.tocMaxHeight = '100';
fixture.detectChanges(); fixture.detectChanges();
expect(tocContainer.styles['max-height']).toBe('100px'); expect(tocContainer!.styles['max-height']).toBe('100px');
}); });
it('should restrain scrolling inside the ToC container', () => { it('should restrain scrolling inside the ToC container', () => {
@ -565,7 +565,7 @@ describe('AppComponent', () => {
expect(restrainScrolling).not.toHaveBeenCalled(); expect(restrainScrolling).not.toHaveBeenCalled();
tocContainer.triggerEventHandler('mousewheel', evt); tocContainer!.triggerEventHandler('mousewheel', evt);
expect(restrainScrolling).toHaveBeenCalledWith(evt); expect(restrainScrolling).toHaveBeenCalledWith(evt);
}); });
}); });
@ -591,7 +591,7 @@ describe('AppComponent', () => {
initializeTest(); initializeTest();
fixture.detectChanges(); fixture.detectChanges();
const banner: HTMLElement = fixture.debugElement.query(By.css('aio-mode-banner')).nativeElement; const banner: HTMLElement = fixture.debugElement.query(By.css('aio-mode-banner')).nativeElement;
expect(banner.textContent.trim()).toEqual(''); expect(banner.textContent!.trim()).toEqual('');
}); });
}); });
@ -985,9 +985,9 @@ describe('AppComponent', () => {
checkHostClass('mode', 'archive'); checkHostClass('mode', 'archive');
}); });
function checkHostClass(type, value) { function checkHostClass(type: string, value: string) {
const host = fixture.debugElement; const host = fixture.debugElement;
const classes = host.properties['className']; const classes: string = host.properties['className'];
const classArray = classes.split(' ').filter(c => c.indexOf(`${type}-`) === 0); const classArray = classes.split(' ').filter(c => c.indexOf(`${type}-`) === 0);
expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`); expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`);
expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`); expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`);
@ -1212,10 +1212,10 @@ class TestHttpClient {
if (/navigation\.json/.test(url)) { if (/navigation\.json/.test(url)) {
data = this.navJson; data = this.navJson;
} else { } else {
const match = /generated\/docs\/(.+)\.json/.exec(url); const match = /generated\/docs\/(.+)\.json/.exec(url)!;
const id = match[1]; const id = match[1]!;
// Make up a title for test purposes // Make up a title for test purposes
const title = id.split('/').pop().replace(/^([a-z])/, (_, letter) => letter.toUpperCase()); const title = id.split('/').pop()!.replace(/^([a-z])/, (_, letter) => letter.toUpperCase());
const h1 = (id === 'no-title') ? '' : `<h1>${title}</h1>`; const h1 = (id === 'no-title') ? '' : `<h1>${title}</h1>`;
const contents = `${h1}<h2 id="#somewhere">Some heading</h2>`; const contents = `${h1}<h2 id="#somewhere">Some heading</h2>`;
data = { id, contents }; data = { id, contents };

View File

@ -152,19 +152,19 @@ export class AppComponent implements OnInit {
this.navigationService.navigationViews.map(views => views['docVersions'])) this.navigationService.navigationViews.map(views => views['docVersions']))
.subscribe(([versionInfo, versions]) => { .subscribe(([versionInfo, versions]) => {
// TODO(pbd): consider whether we can lookup the stable and next versions from the internet // TODO(pbd): consider whether we can lookup the stable and next versions from the internet
const computedVersions = [ const computedVersions: NavigationNode[] = [
{ title: 'next', url: 'https://next.angular.io' }, { title: 'next', url: 'https://next.angular.io' },
{ title: 'stable', url: 'https://angular.io' }, { title: 'stable', url: 'https://angular.io' },
]; ];
if (this.deployment.mode === 'archive') { if (this.deployment.mode === 'archive') {
computedVersions.push({ title: `v${versionInfo.major}`, url: null }); computedVersions.push({ title: `v${versionInfo.major}` });
} }
this.docVersions = [...computedVersions, ...versions]; this.docVersions = [...computedVersions, ...versions];
// Find the current version - eithers title matches the current deployment mode // Find the current version - eithers title matches the current deployment mode
// or its title matches the major version of the current version info // or its title matches the major version of the current version info
this.currentDocVersion = this.docVersions.find(version => this.currentDocVersion = this.docVersions.find(version =>
version.title === this.deployment.mode || version.title === `v${versionInfo.major}`); version.title === this.deployment.mode || version.title === `v${versionInfo.major}`)!;
this.currentDocVersion.title += ` (v${versionInfo.raw})`; this.currentDocVersion.title += ` (v${versionInfo.raw})`;
}); });
@ -232,7 +232,7 @@ export class AppComponent implements OnInit {
} }
@HostListener('window:resize', ['$event.target.innerWidth']) @HostListener('window:resize', ['$event.target.innerWidth'])
onResize(width) { onResize(width: number) {
this.isSideBySide = width > this.sideBySideWidth; this.isSideBySide = width > this.sideBySideWidth;
this.showFloatingToc.next(width > this.showFloatingTocWidth); this.showFloatingToc.next(width > this.showFloatingTocWidth);
} }
@ -252,7 +252,7 @@ export class AppComponent implements OnInit {
} }
// Deal with anchor clicks; climb DOM tree until anchor found (or null) // Deal with anchor clicks; climb DOM tree until anchor found (or null)
let target = eventTarget; let target: HTMLElement|null = eventTarget;
while (target && !(target instanceof HTMLAnchorElement)) { while (target && !(target instanceof HTMLAnchorElement)) {
target = target.parentElement; target = target.parentElement;
} }
@ -335,8 +335,8 @@ export class AppComponent implements OnInit {
// Must wait until now for mat-toolbar to be measurable. // Must wait until now for mat-toolbar to be measurable.
const el = this.hostElement.nativeElement as Element; const el = this.hostElement.nativeElement as Element;
this.tocMaxHeightOffset = this.tocMaxHeightOffset =
el.querySelector('footer').clientHeight + el.querySelector('footer')!.clientHeight +
el.querySelector('.app-toolbar').clientHeight + el.querySelector('.app-toolbar')!.clientHeight +
24; // fudge margin 24; // fudge margin
} }
@ -375,7 +375,7 @@ export class AppComponent implements OnInit {
} }
} }
doSearch(query) { doSearch(query: string) {
this.searchResults = this.searchService.search(query); this.searchResults = this.searchService.search(query);
this.showSearchResults = !!query; this.showSearchResults = !!query;
} }

View File

@ -23,7 +23,7 @@ describe('AppModule', () => {
}); });
it('should provide a list of eagerly-loaded embedded components', () => { it('should provide a list of eagerly-loaded embedded components', () => {
const eagerSelector = Object.keys(componentsMap).find(selector => Array.isArray(componentsMap[selector])); const eagerSelector = Object.keys(componentsMap).find(selector => Array.isArray(componentsMap[selector]))!;
const selectorCount = eagerSelector.split(',').length; const selectorCount = eagerSelector.split(',').length;
expect(eagerSelector).not.toBeNull(); expect(eagerSelector).not.toBeNull();
@ -34,7 +34,7 @@ describe('AppModule', () => {
}); });
it('should provide a list of lazy-loaded embedded components', () => { it('should provide a list of lazy-loaded embedded components', () => {
const lazySelector = Object.keys(componentsMap).find(selector => selector.includes('code-example')); const lazySelector = Object.keys(componentsMap).find(selector => selector.includes('code-example'))!;
const selectorCount = lazySelector.split(',').length; const selectorCount = lazySelector.split(',').length;
expect(lazySelector).not.toBeNull(); expect(lazySelector).not.toBeNull();

View File

@ -2,5 +2,5 @@ export interface DocumentContents {
/** The unique identifier for this document */ /** The unique identifier for this document */
id: string; id: string;
/** The HTML to display in the doc viewer */ /** The HTML to display in the doc viewer */
contents: string; contents: string|null;
} }

View File

@ -50,7 +50,7 @@ describe('DocumentService', () => {
}); });
it('should emit a document each time the location changes', () => { it('should emit a document each time the location changes', () => {
let latestDocument: DocumentContents; let latestDocument: DocumentContents|undefined;
const doc0 = { contents: 'doc 0', id: 'initial/doc' }; const doc0 = { contents: 'doc 0', id: 'initial/doc' };
const doc1 = { contents: 'doc 1', id: 'new/doc' }; const doc1 = { contents: 'doc 1', id: 'new/doc' };
const { docService, locationService } = getServices('initial/doc'); const { docService, locationService } = getServices('initial/doc');
@ -67,7 +67,7 @@ describe('DocumentService', () => {
}); });
it('should emit the not-found document if the document is not found on the server', () => { it('should emit the not-found document if the document is not found on the server', () => {
let currentDocument: DocumentContents; let currentDocument: DocumentContents|undefined;
const notFoundDoc = { id: FILE_NOT_FOUND_ID, contents: '<h1>Page Not Found</h1>' }; const notFoundDoc = { id: FILE_NOT_FOUND_ID, contents: '<h1>Page Not Found</h1>' };
const { docService } = getServices('missing/doc'); const { docService } = getServices('missing/doc');
docService.currentDocument.subscribe(doc => currentDocument = doc); docService.currentDocument.subscribe(doc => currentDocument = doc);
@ -82,7 +82,7 @@ describe('DocumentService', () => {
}); });
it('should emit a hard-coded not-found document if the not-found document is not found on the server', () => { it('should emit a hard-coded not-found document if the not-found document is not found on the server', () => {
let currentDocument: DocumentContents; let currentDocument: DocumentContents|undefined;
const hardCodedNotFoundDoc = { contents: 'Document not found', id: FILE_NOT_FOUND_ID }; const hardCodedNotFoundDoc = { contents: 'Document not found', id: FILE_NOT_FOUND_ID };
const nextDoc = { contents: 'Next Doc', id: 'new/doc' }; const nextDoc = { contents: 'Next Doc', id: 'new/doc' };
const { docService, locationService } = getServices(FILE_NOT_FOUND_ID); const { docService, locationService } = getServices(FILE_NOT_FOUND_ID);
@ -99,7 +99,7 @@ describe('DocumentService', () => {
}); });
it('should use a hard-coded error doc if the request fails (but not cache it)', () => { it('should use a hard-coded error doc if the request fails (but not cache it)', () => {
let latestDocument: DocumentContents; let latestDocument: DocumentContents|undefined;
const doc1 = { contents: 'doc 1' }; const doc1 = { contents: 'doc 1' };
const doc2 = { contents: 'doc 2' }; const doc2 = { contents: 'doc 2' };
const { docService, locationService } = getServices('initial/doc'); const { docService, locationService } = getServices('initial/doc');
@ -107,7 +107,7 @@ describe('DocumentService', () => {
docService.currentDocument.subscribe(doc => latestDocument = doc); docService.currentDocument.subscribe(doc => latestDocument = doc);
httpMock.expectOne({}).flush(null, {status: 500, statusText: 'Server Error'}); httpMock.expectOne({}).flush(null, {status: 500, statusText: 'Server Error'});
expect(latestDocument.id).toEqual(FETCHING_ERROR_ID); expect(latestDocument!.id).toEqual(FETCHING_ERROR_ID);
locationService.go('new/doc'); locationService.go('new/doc');
httpMock.expectOne({}).flush(doc1); httpMock.expectOne({}).flush(doc1);
@ -119,14 +119,14 @@ describe('DocumentService', () => {
}); });
it('should not crash the app if the response is invalid JSON', () => { it('should not crash the app if the response is invalid JSON', () => {
let latestDocument: DocumentContents; let latestDocument: DocumentContents|undefined;
const doc1 = { contents: 'doc 1' }; const doc1 = { contents: 'doc 1' };
const { docService, locationService } = getServices('initial/doc'); const { docService, locationService } = getServices('initial/doc');
docService.currentDocument.subscribe(doc => latestDocument = doc); docService.currentDocument.subscribe(doc => latestDocument = doc);
httpMock.expectOne({}).flush('this is invalid JSON'); httpMock.expectOne({}).flush('this is invalid JSON');
expect(latestDocument.id).toEqual(FETCHING_ERROR_ID); expect(latestDocument!.id).toEqual(FETCHING_ERROR_ID);
locationService.go('new/doc'); locationService.go('new/doc');
httpMock.expectOne({}).flush(doc1); httpMock.expectOne({}).flush(doc1);
@ -134,7 +134,7 @@ describe('DocumentService', () => {
}); });
it('should not make a request to the server if the doc is in the cache already', () => { it('should not make a request to the server if the doc is in the cache already', () => {
let latestDocument: DocumentContents; let latestDocument: DocumentContents|undefined;
let subscription: Subscription; let subscription: Subscription;
const doc0 = { contents: 'doc 0' }; const doc0 = { contents: 'doc 0' };

View File

@ -52,7 +52,7 @@ export class DocumentService {
if ( !this.cache.has(id)) { if ( !this.cache.has(id)) {
this.cache.set(id, this.fetchDocument(id)); this.cache.set(id, this.fetchDocument(id));
} }
return this.cache.get(id); return this.cache.get(id)!;
} }
private fetchDocument(id: string): Observable<DocumentContents> { private fetchDocument(id: string): Observable<DocumentContents> {

View File

@ -127,9 +127,9 @@ describe('EmbedComponentsService', () => {
const componentRefs = service.createComponents(host); const componentRefs = service.createComponents(host);
componentRefs[0].changeDetectorRef.detectChanges(); componentRefs[0].changeDetectorRef.detectChanges();
const barEl = host.querySelector('aio-eager-bar'); const barEl = host.querySelector('aio-eager-bar')!;
expect(barEl['aioEagerBarContent']).toBe(projectedContent); expect((barEl as any)['aioEagerBarContent']).toBe(projectedContent);
expect(barEl.innerHTML).toContain(projectedContent); expect(barEl.innerHTML).toContain(projectedContent);
}); });

View File

@ -111,7 +111,7 @@ export class EmbedComponentsService {
// Hack: Preserve the current element content, because the factory will empty it out. // Hack: Preserve the current element content, because the factory will empty it out.
// Security: The source of this `innerHTML` should always be authored by the documentation // Security: The source of this `innerHTML` should always be authored by the documentation
// team and is considered to be safe. // team and is considered to be safe.
host[contentPropertyName] = host.innerHTML; (host as any)[contentPropertyName] = host.innerHTML;
componentRefs.push(factory.create(this.injector, [], host)); componentRefs.push(factory.create(this.injector, [], host));
} }
}); });
@ -141,7 +141,7 @@ export class EmbedComponentsService {
this.componentFactoriesReady.set(compsOrPath, readyPromise); this.componentFactoriesReady.set(compsOrPath, readyPromise);
} }
return this.componentFactoriesReady.get(compsOrPath); return this.componentFactoriesReady.get(compsOrPath)!;
} }
/** /**

View File

@ -34,7 +34,7 @@ describe('ApiListComponent', () => {
*/ */
function expectFilteredResult(label: string, itemTest: (item: ApiItem) => boolean) { function expectFilteredResult(label: string, itemTest: (item: ApiItem) => boolean) {
component.filteredSections.subscribe(filtered => { component.filteredSections.subscribe(filtered => {
let badItem: ApiItem; let badItem: ApiItem|undefined;
expect(filtered.length).toBeGreaterThan(0, 'expected something'); expect(filtered.length).toBeGreaterThan(0, 'expected something');
expect(filtered.every(section => section.items.every( expect(filtered.every(section => section.items.every(
item => { item => {
@ -53,7 +53,7 @@ describe('ApiListComponent', () => {
}); });
it('should return all complete sections when no criteria', () => { it('should return all complete sections when no criteria', () => {
let filtered: ApiSection[]; let filtered: ApiSection[]|undefined;
component.filteredSections.subscribe(f => filtered = f); component.filteredSections.subscribe(f => filtered = f);
expect(filtered).toEqual(sections); expect(filtered).toEqual(sections);
}); });
@ -68,7 +68,7 @@ describe('ApiListComponent', () => {
component.filteredSections.subscribe(filtered => { component.filteredSections.subscribe(filtered => {
expect(filtered.length).toBe(1, 'only one section'); expect(filtered.length).toBe(1, 'only one section');
expect(filtered[0].name).toBe('core'); expect(filtered[0].name).toBe('core');
expect(filtered[0].items.every(item => item.show)).toBe(true, 'all core items shown'); expect(filtered[0].items.every(item => !!item.show)).toBe(true, 'all core items shown');
}); });
}); });

View File

@ -35,8 +35,8 @@ describe('ApiService', () => {
let completed = false; let completed = false;
service.sections.subscribe( service.sections.subscribe(
null, undefined,
null, undefined,
() => completed = true () => completed = true
); );

View File

@ -41,7 +41,7 @@ export class CodeExampleComponent implements OnInit {
const element: HTMLElement = this.elementRef.nativeElement; const element: HTMLElement = this.elementRef.nativeElement;
this.language = element.getAttribute('language') || ''; this.language = element.getAttribute('language') || '';
this.linenums = element.getAttribute('linenums'); this.linenums = element.getAttribute('linenums') || '';
this.path = element.getAttribute('path') || ''; this.path = element.getAttribute('path') || '';
this.region = element.getAttribute('region') || ''; this.region = element.getAttribute('region') || '';
this.title = element.getAttribute('title') || ''; this.title = element.getAttribute('title') || '';

View File

@ -2,13 +2,13 @@
import { Component, ElementRef, OnInit } from '@angular/core'; import { Component, ElementRef, OnInit } from '@angular/core';
export interface TabInfo { export interface TabInfo {
class: string; class: string|null;
code: string; code: string;
language: string; language: string|null;
linenums: any; linenums: any;
path: string; path: string;
region: string; region: string;
title: string; title: string|null;
} }
/** /**

View File

@ -36,8 +36,8 @@ describe('CodeComponent', () => {
// we take strict measures to wipe it out in the `afterAll` // we take strict measures to wipe it out in the `afterAll`
// and make sure THAT runs after the tests by making component creation async // and make sure THAT runs after the tests by making component creation async
afterAll(() => { afterAll(() => {
delete window['prettyPrint']; delete (window as any)['prettyPrint'];
delete window['prettyPrintOne']; delete (window as any)['prettyPrintOne'];
}); });
beforeEach(() => { beforeEach(() => {

View File

@ -170,7 +170,7 @@ export class CodeComponent implements OnChanges {
} }
} }
function leftAlign(text) { function leftAlign(text: string) {
let indent = Number.MAX_VALUE; let indent = Number.MAX_VALUE;
const lines = text.split('\n'); const lines = text.split('\n');
lines.forEach(line => { lines.forEach(line => {

View File

@ -7,7 +7,9 @@ import 'rxjs/add/operator/first';
import { Logger } from 'app/shared/logger.service'; import { Logger } from 'app/shared/logger.service';
declare const System; declare const System: {
import(name: string): Promise<any>;
};
type PrettyPrintOne = (code: string, language?: string, linenums?: number | boolean) => string; type PrettyPrintOne = (code: string, language?: string, linenums?: number | boolean) => string;
@ -24,12 +26,12 @@ export class PrettyPrinter {
} }
private getPrettyPrintOne(): Promise<PrettyPrintOne> { private getPrettyPrintOne(): Promise<PrettyPrintOne> {
const ppo = window['prettyPrintOne']; const ppo = (window as any)['prettyPrintOne'];
return ppo ? Promise.resolve(ppo) : return ppo ? Promise.resolve(ppo) :
// prettify.js is not in window global; load it with webpack loader // prettify.js is not in window global; load it with webpack loader
System.import('assets/js/prettify.js') System.import('assets/js/prettify.js')
.then( .then(
() => window['prettyPrintOne'], () => (window as any)['prettyPrintOne'],
err => { err => {
const msg = 'Cannot get prettify.js from server'; const msg = 'Cannot get prettify.js from server';
this.logger.error(msg, err); this.logger.error(msg, err);

View File

@ -38,7 +38,7 @@ export class ContributorListComponent implements OnInit {
}); });
} }
selectGroup(name) { selectGroup(name: string) {
name = name.toLowerCase(); name = name.toLowerCase();
this.selectedGroup = this.groups.find(g => g.name.toLowerCase() === name) || this.groups[0]; this.selectedGroup = this.groups.find(g => g.name.toLowerCase() === name) || this.groups[0];
this.locationService.setSearch('', {group: this.selectedGroup.name}); this.locationService.setSearch('', {group: this.selectedGroup.name});

View File

@ -40,7 +40,7 @@ export class ContributorComponent {
noPicture = '_no-one.png'; noPicture = '_no-one.png';
pictureBase = CONTENT_URL_PREFIX + 'images/bios/'; pictureBase = CONTENT_URL_PREFIX + 'images/bios/';
flipCard(person) { flipCard(person: Contributor) {
person.isFlipped = !person.isFlipped; person.isFlipped = !person.isFlipped;
} }
} }

View File

@ -43,7 +43,7 @@ describe('ContributorService', () => {
it('contributors observable should complete', () => { it('contributors observable should complete', () => {
let completed = false; let completed = false;
contribService.contributors.subscribe(null, null, () => completed = true); contribService.contributors.subscribe(undefined, undefined, () => completed = true);
expect(true).toBe(true, 'observable completed'); expect(true).toBe(true, 'observable completed');
}); });

View File

@ -23,7 +23,7 @@ export class ContributorService {
const contributors = this.http.get<{[key: string]: Contributor}>(contributorsPath) const contributors = this.http.get<{[key: string]: Contributor}>(contributorsPath)
// Create group map // Create group map
.map(contribs => { .map(contribs => {
const contribMap = new Map<string, Contributor[]>(); const contribMap: { [name: string]: Contributor[]} = {};
Object.keys(contribs).forEach(key => { Object.keys(contribs).forEach(key => {
const contributor = contribs[key]; const contributor = contribs[key];
const group = contributor.group; const group = contributor.group;

View File

@ -13,7 +13,7 @@ describe('LiveExampleComponent', () => {
let liveExampleComponent: LiveExampleComponent; let liveExampleComponent: LiveExampleComponent;
let fixture: ComponentFixture<HostComponent>; let fixture: ComponentFixture<HostComponent>;
let testPath: string; let testPath: string;
let liveExampleContent: string; let liveExampleContent: string|null;
//////// test helpers //////// //////// test helpers ////////
@ -66,7 +66,7 @@ describe('LiveExampleComponent', () => {
.overrideComponent(EmbeddedPlunkerComponent, {set: {template: 'NO IFRAME'}}); .overrideComponent(EmbeddedPlunkerComponent, {set: {template: 'NO IFRAME'}});
testPath = defaultTestPath; testPath = defaultTestPath;
liveExampleContent = undefined; liveExampleContent = null;
}); });
describe('when not embedded', () => { describe('when not embedded', () => {

View File

@ -94,7 +94,7 @@ export class LiveExampleComponent implements OnInit {
let exampleDir = attrs.name; let exampleDir = attrs.name;
if (!exampleDir) { if (!exampleDir) {
// take last segment, excluding hash fragment and query params // take last segment, excluding hash fragment and query params
exampleDir = location.path(false).match(/[^\/?\#]+(?=\/?(?:$|\#|\?))/)[0]; exampleDir = (location.path(false).match(/[^\/?\#]+(?=\/?(?:$|\#|\?))/) || [])[0];
} }
this.exampleDir = exampleDir.trim(); this.exampleDir = exampleDir.trim();
this.zipName = exampleDir.indexOf('/') === -1 ? this.exampleDir : exampleDir.split('/')[0]; this.zipName = exampleDir.indexOf('/') === -1 ? this.exampleDir : exampleDir.split('/')[0];
@ -150,7 +150,7 @@ export class LiveExampleComponent implements OnInit {
} }
@HostListener('window:resize', ['$event.target.innerWidth']) @HostListener('window:resize', ['$event.target.innerWidth'])
onResize(width) { onResize(width: number) {
if (this.mode !== 'downloadOnly') { if (this.mode !== 'downloadOnly') {
this.calcPlnkrLink(width); this.calcPlnkrLink(width);
} }

View File

@ -43,7 +43,7 @@ describe('ResourceService', () => {
it('categories observable should complete', () => { it('categories observable should complete', () => {
let completed = false; let completed = false;
resourceService.categories.subscribe(null, null, () => completed = true); resourceService.categories.subscribe(undefined, undefined, () => completed = true);
expect(true).toBe(true, 'observable completed'); expect(true).toBe(true, 'observable completed');
}); });

View File

@ -43,7 +43,7 @@ describe('DocViewerComponent', () => {
describe('#doc', () => { describe('#doc', () => {
let renderSpy: jasmine.Spy; let renderSpy: jasmine.Spy;
const setCurrentDoc = (contents, id = 'fizz/buzz') => { const setCurrentDoc = (contents: string|null, id = 'fizz/buzz') => {
parentComponent.currentDoc = {contents, id}; parentComponent.currentDoc = {contents, id};
parentFixture.detectChanges(); parentFixture.detectChanges();
}; };
@ -432,7 +432,7 @@ describe('DocViewerComponent', () => {
}); });
it('should store the embedded components', async () => { it('should store the embedded components', async () => {
const embeddedComponents = []; const embeddedComponents: ComponentRef<any>[] = [];
embedIntoSpy.and.returnValue(of(embeddedComponents)); embedIntoSpy.and.returnValue(of(embeddedComponents));
await doRender('Some content'); await doRender('Some content');
@ -678,7 +678,7 @@ describe('DocViewerComponent', () => {
describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => { describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => {
beforeEach(() => docViewerEl.classList[noAnimations ? 'add' : 'remove'](NO_ANIMATIONS)); beforeEach(() => docViewerEl.classList[noAnimations ? 'add' : 'remove'](NO_ANIMATIONS));
it('should return an observable', done => { it('should return an observable', (done: DoneFn) => {
docViewer.swapViews().subscribe(done, done.fail); docViewer.swapViews().subscribe(done, done.fail);
}); });

View File

@ -114,12 +114,12 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
const hasToc = !!titleEl && !/no-?toc/i.test(titleEl.className); const hasToc = !!titleEl && !/no-?toc/i.test(titleEl.className);
if (hasToc) { if (hasToc) {
titleEl.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>'); titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
} }
return () => { return () => {
this.tocService.reset(); this.tocService.reset();
let title = ''; let title: string|null = '';
// Only create ToC for docs with an `<h1>` heading. // Only create ToC for docs with an `<h1>` heading.
// If you don't want a ToC, add "no-toc" class to `<h1>`. // If you don't want a ToC, add "no-toc" class to `<h1>`.
@ -169,7 +169,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
* entering animation has been completed. This is useful for work that needs to be done as soon as * entering animation has been completed. This is useful for work that needs to be done as soon as
* the element has been attached to the DOM. * the element has been attached to the DOM.
*/ */
protected swapViews(onInsertedCb = () => undefined): Observable<void> { protected swapViews(onInsertedCb = () => {}): Observable<void> {
const raf$ = new Observable<void>(subscriber => { const raf$ = new Observable<void>(subscriber => {
const rafId = requestAnimationFrame(() => { const rafId = requestAnimationFrame(() => {
subscriber.next(); subscriber.next();
@ -182,15 +182,18 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
// According to the [CSSOM spec](https://drafts.csswg.org/cssom/#serializing-css-values), // According to the [CSSOM spec](https://drafts.csswg.org/cssom/#serializing-css-values),
// `time` values should be returned in seconds. // `time` values should be returned in seconds.
const getActualDuration = (elem: HTMLElement) => { const getActualDuration = (elem: HTMLElement) => {
const cssValue = getComputedStyle(elem).transitionDuration; const cssValue = getComputedStyle(elem).transitionDuration || '';
const seconds = Number(cssValue.replace(/s$/, '')); const seconds = Number(cssValue.replace(/s$/, ''));
return 1000 * seconds; return 1000 * seconds;
}; };
const animateProp = const animateProp =
(elem: HTMLElement, prop: string, from: string, to: string, duration = 200) => { (elem: HTMLElement, prop: keyof CSSStyleDeclaration, from: string, to: string, duration = 200) => {
const animationsDisabled = !DocViewerComponent.animationsEnabled const animationsDisabled = !DocViewerComponent.animationsEnabled
|| this.hostElement.classList.contains(NO_ANIMATIONS); || this.hostElement.classList.contains(NO_ANIMATIONS);
if (prop === 'length' || prop === 'parentRule') {
// We cannot animate length or parentRule properties because they are readonly
return this.void$;
}
elem.style.transition = ''; elem.style.transition = '';
return animationsDisabled return animationsDisabled
? this.void$.do(() => elem.style[prop] = to) ? this.void$.do(() => elem.style[prop] = to)
@ -201,7 +204,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
// setting each style. // setting each style.
.switchMap(() => raf$).do(() => elem.style[prop] = from) .switchMap(() => raf$).do(() => elem.style[prop] = from)
.switchMap(() => raf$).do(() => elem.style.transition = `all ${duration}ms ease-in-out`) .switchMap(() => raf$).do(() => elem.style.transition = `all ${duration}ms ease-in-out`)
.switchMap(() => raf$).do(() => elem.style[prop] = to) .switchMap(() => raf$).do(() => (elem.style as any)[prop] = to)
.switchMap(() => timer(getActualDuration(elem))).switchMap(() => this.void$); .switchMap(() => timer(getActualDuration(elem))).switchMap(() => this.void$);
}; };
@ -214,7 +217,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
done$ = done$ done$ = done$
// Remove the current view from the viewer. // Remove the current view from the viewer.
.switchMap(() => animateLeave(this.currViewContainer)) .switchMap(() => animateLeave(this.currViewContainer))
.do(() => this.currViewContainer.parentElement.removeChild(this.currViewContainer)) .do(() => this.currViewContainer.parentElement!.removeChild(this.currViewContainer))
.do(() => this.docRemoved.emit()); .do(() => this.docRemoved.emit());
} }

View File

@ -73,18 +73,18 @@ describe('TocComponent', () => {
it('should update when the TocItems are updated', () => { it('should update when the TocItems are updated', () => {
tocService.tocList.next([tocItem('Heading A')]); tocService.tocList.next([tocItem('Heading A')]);
fixture.detectChanges(); fixture.detectChanges();
expect(tocComponentDe.queryAllNodes(By.css('li')).length).toBe(1); expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
tocService.tocList.next([tocItem('Heading A'), tocItem('Heading B'), tocItem('Heading C')]); tocService.tocList.next([tocItem('Heading A'), tocItem('Heading B'), tocItem('Heading C')]);
fixture.detectChanges(); fixture.detectChanges();
expect(tocComponentDe.queryAllNodes(By.css('li')).length).toBe(3); expect(tocComponentDe.queryAll(By.css('li')).length).toBe(3);
}); });
it('should only display H2 and H3 TocItems', () => { it('should only display H2 and H3 TocItems', () => {
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]); tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C', 'h3')]);
fixture.detectChanges(); fixture.detectChanges();
const tocItems = tocComponentDe.queryAllNodes(By.css('li')); const tocItems = tocComponentDe.queryAll(By.css('li'));
const textContents = tocItems.map(item => item.nativeNode.textContent.trim()); const textContents = tocItems.map(item => item.nativeNode.textContent.trim());
expect(tocItems.length).toBe(2); expect(tocItems.length).toBe(2);
@ -97,12 +97,12 @@ describe('TocComponent', () => {
it('should stop listening for TocItems once destroyed', () => { it('should stop listening for TocItems once destroyed', () => {
tocService.tocList.next([tocItem('Heading A')]); tocService.tocList.next([tocItem('Heading A')]);
fixture.detectChanges(); fixture.detectChanges();
expect(tocComponentDe.queryAllNodes(By.css('li')).length).toBe(1); expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
tocComponent.ngOnDestroy(); tocComponent.ngOnDestroy();
tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C')]); tocService.tocList.next([tocItem('Heading A', 'h1'), tocItem('Heading B'), tocItem('Heading C')]);
fixture.detectChanges(); fixture.detectChanges();
expect(tocComponentDe.queryAllNodes(By.css('li')).length).toBe(1); expect(tocComponentDe.queryAll(By.css('li')).length).toBe(1);
}); });
describe('when fewer than `maxPrimary` TocItems', () => { describe('when fewer than `maxPrimary` TocItems', () => {
@ -339,7 +339,7 @@ describe('TocComponent', () => {
it('should re-apply the `active` class when the list elements change', () => { it('should re-apply the `active` class when the list elements change', () => {
const getActiveTextContent = () => const getActiveTextContent = () =>
page.listItems.find(By.css('.active')).nativeElement.textContent.trim(); page.listItems.find(By.css('.active'))!.nativeElement.textContent.trim();
tocComponent.activeIndex = 1; tocComponent.activeIndex = 1;
fixture.detectChanges(); fixture.detectChanges();
@ -462,7 +462,7 @@ class TestScrollService {
class TestTocService { class TestTocService {
tocList = new BehaviorSubject<TocItem[]>(getTestTocList()); tocList = new BehaviorSubject<TocItem[]>(getTestTocList());
activeItemIndex = new BehaviorSubject<number | null>(null); activeItemIndex = new BehaviorSubject<number | null>(null);
setActiveIndex(index) { setActiveIndex(index: number|null) {
this.activeItemIndex.next(index); this.activeItemIndex.next(index);
if (asap.scheduled) { if (asap.scheduled) {
asap.flush(); asap.flush();

View File

@ -93,6 +93,6 @@ export class TocComponent implements OnInit, AfterViewInit, OnDestroy {
} }
} }
function count<T>(array: T[], fn: (T) => boolean) { function count<T>(array: T[], fn: (item: T) => boolean) {
return array.reduce((count, item) => fn(item) ? count + 1 : count, 0); return array.reduce((count, item) => fn(item) ? count + 1 : count, 0);
} }

View File

@ -45,7 +45,7 @@ describe('NavigationService', () => {
it('navigationViews observable should complete', () => { it('navigationViews observable should complete', () => {
let completed = false; let completed = false;
navService.navigationViews.subscribe(null, null, () => completed = true); navService.navigationViews.subscribe(undefined, undefined, () => completed = true);
expect(true).toBe(true, 'observable completed'); expect(true).toBe(true, 'observable completed');
// Stop `$httpMock.verify()` from complaining. // Stop `$httpMock.verify()` from complaining.
@ -53,15 +53,15 @@ describe('NavigationService', () => {
}); });
it('should return the same object to all subscribers', () => { it('should return the same object to all subscribers', () => {
let views1: NavigationViews; let views1: NavigationViews|undefined;
navService.navigationViews.subscribe(views => views1 = views); navService.navigationViews.subscribe(views => views1 = views);
let views2: NavigationViews; let views2: NavigationViews|undefined;
navService.navigationViews.subscribe(views => views2 = views); navService.navigationViews.subscribe(views => views2 = views);
httpMock.expectOne({}).flush({ TopBar: [{ url: 'a' }] }); httpMock.expectOne({}).flush({ TopBar: [{ url: 'a' }] });
let views3: NavigationViews; let views3: NavigationViews|undefined;
navService.navigationViews.subscribe(views => views3 = views); navService.navigationViews.subscribe(views => views3 = views);
expect(views2).toBe(views1); expect(views2).toBe(views1);
@ -143,7 +143,7 @@ describe('NavigationService', () => {
url: 'b', url: 'b',
view: 'SideNav', view: 'SideNav',
nodes: [ nodes: [
sideNavNodes[0].children[0], sideNavNodes[0].children![0],
sideNavNodes[0] sideNavNodes[0]
] ]
} }
@ -155,8 +155,8 @@ describe('NavigationService', () => {
url: 'd', url: 'd',
view: 'SideNav', view: 'SideNav',
nodes: [ nodes: [
sideNavNodes[0].children[0].children[1], sideNavNodes[0].children![0].children![1],
sideNavNodes[0].children[0], sideNavNodes[0].children![0],
sideNavNodes[0] sideNavNodes[0]
] ]
} }
@ -200,8 +200,8 @@ describe('NavigationService', () => {
url: 'c', url: 'c',
view: 'SideNav', view: 'SideNav',
nodes: [ nodes: [
sideNavNodes[0].children[0].children[0], sideNavNodes[0].children![0].children![0],
sideNavNodes[0].children[0], sideNavNodes[0].children![0],
sideNavNodes[0] sideNavNodes[0]
] ]
} }

View File

@ -97,7 +97,7 @@ export class NavigationService {
(navMap, url) => { (navMap, url) => {
const urlKey = url.startsWith('api/') ? 'api' : url; const urlKey = url.startsWith('api/') ? 'api' : url;
return navMap[urlKey] || { '' : { view: '', url: urlKey, nodes: [] }}; return navMap.get(urlKey) || { '' : { view: '', url: urlKey, nodes: [] }};
}) })
.publishReplay(1); .publishReplay(1);
currentNodes.connect(); currentNodes.connect();
@ -145,7 +145,10 @@ export class NavigationService {
if (url) { if (url) {
// Strip off trailing slashes from nodes in the navMap - they are not relevant to matching // Strip off trailing slashes from nodes in the navMap - they are not relevant to matching
const cleanedUrl = url.replace(/\/$/, ''); const cleanedUrl = url.replace(/\/$/, '');
const navMapItem = navMap[cleanedUrl] = navMap[cleanedUrl] || {}; if (!navMap.has(cleanedUrl)) {
navMap.set(cleanedUrl, {});
}
const navMapItem = navMap.get(cleanedUrl)!;
navMapItem[view] = { url, view, nodes }; navMapItem[view] = { url, view, nodes };
} }

View File

@ -56,7 +56,7 @@ describe('SearchService', () => {
it('should push the response to the returned observable', () => { it('should push the response to the returned observable', () => {
const mockSearchResults = { results: ['a', 'b'] }; const mockSearchResults = { results: ['a', 'b'] };
let actualSearchResults; let actualSearchResults: any;
(mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults)); (mockWorker.sendMessage as jasmine.Spy).and.returnValue(Observable.of(mockSearchResults));
service.search('some query').subscribe(results => actualSearchResults = results); service.search('some query').subscribe(results => actualSearchResults = results);
expect(actualSearchResults).toEqual(mockSearchResults); expect(actualSearchResults).toEqual(mockSearchResults);

View File

@ -8,29 +8,26 @@ describe('Attribute Utilities', () => {
beforeEach(() => { beforeEach(() => {
const div = document.createElement('div'); const div = document.createElement('div');
div.innerHTML = `<div a b="true" c="false" D="foo" d-E></div>`; div.innerHTML = `<div a b="true" c="false" D="foo" d-E></div>`;
testEl = div.querySelector('div'); testEl = div.querySelector('div')!;
}); });
describe('getAttrs', () => { describe('getAttrs', () => {
const expectedMap = {
beforeEach(() => {
this.expectedMap = {
a: '', a: '',
b: 'true', b: 'true',
c: 'false', c: 'false',
d: 'foo', d: 'foo',
'd-e': '' 'd-e': ''
}; };
});
it('should get attr map from getAttrs(element)', () => { it('should get attr map from getAttrs(element)', () => {
const actual = getAttrs(testEl); const actual = getAttrs(testEl);
expect(actual).toEqual(this.expectedMap); expect(actual).toEqual(expectedMap);
}); });
it('should get attr map from getAttrs(elementRef)', () => { it('should get attr map from getAttrs(elementRef)', () => {
const actual = getAttrs(new ElementRef(testEl)); const actual = getAttrs(new ElementRef(testEl));
expect(actual).toEqual(this.expectedMap); expect(actual).toEqual(expectedMap);
}); });
}); });

View File

@ -10,7 +10,7 @@ interface StringMap { [index: string]: string; }
*/ */
export function getAttrs(el: HTMLElement | ElementRef): StringMap { export function getAttrs(el: HTMLElement | ElementRef): StringMap {
const attrs: NamedNodeMap = el instanceof ElementRef ? el.nativeElement.attributes : el.attributes; const attrs: NamedNodeMap = el instanceof ElementRef ? el.nativeElement.attributes : el.attributes;
const attrMap = {}; const attrMap: StringMap = {};
for (const attr of attrs as any /* cast due to https://github.com/Microsoft/TypeScript/issues/2695 */) { for (const attr of attrs as any /* cast due to https://github.com/Microsoft/TypeScript/issues/2695 */) {
attrMap[attr.name.toLowerCase()] = attr.value; attrMap[attr.name.toLowerCase()] = attr.value;
} }
@ -24,7 +24,7 @@ export function getAttrs(el: HTMLElement | ElementRef): StringMap {
export function getAttrValue(attrs: StringMap, attr: string | string[] = ''): string { export function getAttrValue(attrs: StringMap, attr: string | string[] = ''): string {
return attrs[typeof attr === 'string' ? return attrs[typeof attr === 'string' ?
attr.toLowerCase() : attr.toLowerCase() :
attr.find(a => attrs[a.toLowerCase()] !== undefined) attr.find(a => attrs[a.toLowerCase()] !== undefined) || ''
]; ];
} }
@ -33,7 +33,7 @@ export function getAttrValue(attrs: StringMap, attr: string | string[] = ''): st
* @param attrValue The string value of some attribute (or undefined if attribute not present) * @param attrValue The string value of some attribute (or undefined if attribute not present)
* @param def Default boolean value when attribute is undefined. * @param def Default boolean value when attribute is undefined.
*/ */
export function boolFromValue(attrValue: string, def: boolean = false) { export function boolFromValue(attrValue: string|undefined, def: boolean = false) {
// tslint:disable-next-line:triple-equals // tslint:disable-next-line:triple-equals
return attrValue == undefined ? def : attrValue.trim() !== 'false'; return attrValue == undefined ? def : attrValue.trim() !== 'false';
} }

View File

@ -9,7 +9,7 @@
export class CopierService { export class CopierService {
private fakeElem: HTMLTextAreaElement; private fakeElem: HTMLTextAreaElement|null;
/** /**
* Creates a fake textarea element, sets its value from `text` property, * Creates a fake textarea element, sets its value from `text` property,

View File

@ -11,7 +11,7 @@ describe('CustomIconRegistry', () => {
{ name: 'test_icon', svgSource: svgSrc } { name: 'test_icon', svgSource: svgSrc }
]; ];
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, svgIcons); const registry = new CustomIconRegistry(mockHttp, mockSanitizer, svgIcons);
let svgElement: SVGElement; let svgElement: SVGElement|undefined;
registry.getNamedSvgIcon('test_icon').subscribe(el => svgElement = el); registry.getNamedSvgIcon('test_icon').subscribe(el => svgElement = el);
expect(svgElement).toEqual(createSvg(svgSrc)); expect(svgElement).toEqual(createSvg(svgSrc));
}); });
@ -36,8 +36,8 @@ describe('CustomIconRegistry', () => {
}); });
}); });
function createSvg(svgSrc) { function createSvg(svgSrc: string): SVGElement {
const div = document.createElement('div'); const div = document.createElement('div');
div.innerHTML = svgSrc; div.innerHTML = svgSrc;
return div.querySelector('svg'); return div.querySelector('svg')!;
} }

View File

@ -52,7 +52,7 @@ export class CustomIconRegistry extends MatIconRegistry {
svgIcons.forEach(icon => { svgIcons.forEach(icon => {
// SECURITY: the source for the SVG icons is provided in code by trusted developers // SECURITY: the source for the SVG icons is provided in code by trusted developers
div.innerHTML = icon.svgSource; div.innerHTML = icon.svgSource;
this.preloadedSvgElements[icon.name] = div.querySelector('svg'); this.preloadedSvgElements[icon.name] = div.querySelector('svg')!;
}); });
} }
} }

View File

@ -29,7 +29,7 @@ export class GaService {
this.ga('send', 'pageview'); this.ga('send', 'pageview');
} }
ga(...args) { ga(...args: any[]) {
this.window['ga'](...args); (this.window as any)['ga'](...args);
} }
} }

View File

@ -39,7 +39,7 @@ describe('LocationService', () => {
location.simulatePopState('/next-url2'); location.simulatePopState('/next-url2');
location.simulatePopState('/next-url3'); location.simulatePopState('/next-url3');
let initialUrl; let initialUrl: string|undefined;
service.currentUrl.subscribe(url => initialUrl = url); service.currentUrl.subscribe(url => initialUrl = url);
expect(initialUrl).toEqual('next-url3'); expect(initialUrl).toEqual('next-url3');
}); });
@ -49,7 +49,7 @@ describe('LocationService', () => {
location.simulatePopState('/initial-url2'); location.simulatePopState('/initial-url2');
location.simulatePopState('/initial-url3'); location.simulatePopState('/initial-url3');
const urls = []; const urls: string[] = [];
service.currentUrl.subscribe(url => urls.push(url)); service.currentUrl.subscribe(url => urls.push(url));
location.simulatePopState('/next-url1'); location.simulatePopState('/next-url1');
@ -69,13 +69,13 @@ describe('LocationService', () => {
location.simulatePopState('/initial-url2'); location.simulatePopState('/initial-url2');
location.simulatePopState('/initial-url3'); location.simulatePopState('/initial-url3');
const urls1 = []; const urls1: string[] = [];
service.currentUrl.subscribe(url => urls1.push(url)); service.currentUrl.subscribe(url => urls1.push(url));
location.simulatePopState('/next-url1'); location.simulatePopState('/next-url1');
location.simulatePopState('/next-url2'); location.simulatePopState('/next-url2');
const urls2 = []; const urls2: string[] = [];
service.currentUrl.subscribe(url => urls2.push(url)); service.currentUrl.subscribe(url => urls2.push(url));
location.simulatePopState('/next-url3'); location.simulatePopState('/next-url3');
@ -150,7 +150,7 @@ describe('LocationService', () => {
}); });
it('should strip the query off the url', () => { it('should strip the query off the url', () => {
let path: string; let path: string|undefined;
service.currentPath.subscribe(p => path = p); service.currentPath.subscribe(p => path = p);
@ -182,7 +182,7 @@ describe('LocationService', () => {
location.simulatePopState('/next/url2'); location.simulatePopState('/next/url2');
location.simulatePopState('/next/url3'); location.simulatePopState('/next/url3');
let initialPath: string; let initialPath: string|undefined;
service.currentPath.subscribe(path => initialPath = path); service.currentPath.subscribe(path => initialPath = path);
expect(initialPath).toEqual('next/url3'); expect(initialPath).toEqual('next/url3');
@ -247,7 +247,7 @@ describe('LocationService', () => {
}); });
it('should emit the new url', () => { it('should emit the new url', () => {
const urls = []; const urls: string[] = [];
service.go('some-initial-url'); service.go('some-initial-url');
service.currentUrl.subscribe(url => urls.push(url)); service.currentUrl.subscribe(url => urls.push(url));
@ -259,7 +259,7 @@ describe('LocationService', () => {
}); });
it('should strip leading and trailing slashes', () => { it('should strip leading and trailing slashes', () => {
let url: string; let url: string|undefined;
service.currentUrl.subscribe(u => url = u); service.currentUrl.subscribe(u => url = u);
service.go('/some/url/'); service.go('/some/url/');
@ -269,23 +269,18 @@ describe('LocationService', () => {
expect(url).toBe('some/url'); expect(url).toBe('some/url');
}); });
it('should ignore undefined URL string', noUrlTest(undefined)); it('should ignore empty URL string', () => {
it('should ignore null URL string', noUrlTest(null));
it('should ignore empty URL string', noUrlTest(''));
function noUrlTest(testUrl: string) {
return function() {
const initialUrl = 'some/url'; const initialUrl = 'some/url';
const goExternalSpy = spyOn(service, 'goExternal'); const goExternalSpy = spyOn(service, 'goExternal');
let url: string; let url: string|undefined;
service.go(initialUrl); service.go(initialUrl);
service.currentUrl.subscribe(u => url = u); service.currentUrl.subscribe(u => url = u);
service.go(testUrl); service.go('');
expect(url).toEqual(initialUrl, 'should not have re-navigated locally'); expect(url).toEqual(initialUrl, 'should not have re-navigated locally');
expect(goExternalSpy).not.toHaveBeenCalled(); expect(goExternalSpy).not.toHaveBeenCalled();
}; });
}
it('should leave the site for external url that starts with "http"', () => { it('should leave the site for external url that starts with "http"', () => {
const goExternalSpy = spyOn(service, 'goExternal'); const goExternalSpy = spyOn(service, 'goExternal');
@ -310,7 +305,7 @@ describe('LocationService', () => {
}); });
it('should not update currentUrl for external url that starts with "http"', () => { it('should not update currentUrl for external url that starts with "http"', () => {
let localUrl: string; let localUrl: string|undefined;
spyOn(service, 'goExternal'); spyOn(service, 'goExternal');
service.currentUrl.subscribe(url => localUrl = url); service.currentUrl.subscribe(url => localUrl = url);
service.go('https://some/far/away/land'); service.go('https://some/far/away/land');

View File

@ -18,7 +18,7 @@ export class LocationService {
.map(url => this.stripSlashes(url)); .map(url => this.stripSlashes(url));
currentPath = this.currentUrl currentPath = this.currentUrl
.map(url => url.match(/[^?#]*/)[0]) // strip query and hash .map(url => (url.match(/[^?#]*/) || [])[0]) // strip query and hash
.do(path => this.gaService.locationChanged(path)); .do(path => this.gaService.locationChanged(path));
constructor( constructor(
@ -30,14 +30,14 @@ export class LocationService {
this.urlSubject.next(location.path(true)); this.urlSubject.next(location.path(true));
this.location.subscribe(state => { this.location.subscribe(state => {
return this.urlSubject.next(state.url); return this.urlSubject.next(state.url || '');
}); });
swUpdates.updateActivated.subscribe(() => this.swUpdateActivated = true); swUpdates.updateActivated.subscribe(() => this.swUpdateActivated = true);
} }
// TODO?: ignore if url-without-hash-or-search matches current location? // TODO?: ignore if url-without-hash-or-search matches current location?
go(url: string) { go(url: string|null|undefined) {
if (!url) { return; } if (!url) { return; }
url = this.stripSlashes(url); url = this.stripSlashes(url);
if (/^http/.test(url) || this.swUpdateActivated) { if (/^http/.test(url) || this.swUpdateActivated) {
@ -62,8 +62,8 @@ export class LocationService {
return url.replace(/^\/+/, '').replace(/\/+(\?|#|$)/, '$1'); return url.replace(/^\/+/, '').replace(/\/+(\?|#|$)/, '$1');
} }
search(): { [index: string]: string; } { search() {
const search = {}; const search: { [index: string]: string|undefined; } = {};
const path = this.location.path(); const path = this.location.path();
const q = path.indexOf('?'); const q = path.indexOf('?');
if (q > -1) { if (q > -1) {
@ -80,11 +80,10 @@ export class LocationService {
return search; return search;
} }
setSearch(label: string, params: {}) { setSearch(label: string, params: { [key: string]: string|undefined}) {
const search = Object.keys(params).reduce((acc, key) => { const search = Object.keys(params).reduce((acc, key) => {
const value = params[key]; const value = params[key];
// tslint:disable-next-line:triple-equals return (value === undefined) ? acc :
return value == undefined ? acc :
acc += (acc ? '&' : '?') + `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; acc += (acc ? '&' : '?') + `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}, ''); }, '');

View File

@ -5,17 +5,17 @@ import { environment } from '../../environments/environment';
@Injectable() @Injectable()
export class Logger { export class Logger {
log(value: any, ...rest) { log(value: any, ...rest: any[]) {
if (!environment.production) { if (!environment.production) {
console.log(value, ...rest); console.log(value, ...rest);
} }
} }
error(value: any, ...rest) { error(value: any, ...rest: any[]) {
console.error(value, ...rest); console.error(value, ...rest);
} }
warn(value: any, ...rest) { warn(value: any, ...rest: any[]) {
console.warn(value, ...rest); console.warn(value, ...rest);
} }
} }

View File

@ -60,14 +60,15 @@ describe('ScrollSpiedElementGroup', () => {
describe('#onScroll()', () => { describe('#onScroll()', () => {
let group: ScrollSpiedElementGroup; let group: ScrollSpiedElementGroup;
let activeItems: ScrollItem[]; let activeItems: (ScrollItem|null)[];
const activeIndices = () => activeItems.map(x => x && x.index); const activeIndices = () => activeItems.map(x => x && x.index);
beforeEach(() => { beforeEach(() => {
const tops = [50, 150, 100]; const tops = [50, 150, 100];
spyOn(ScrollSpiedElement.prototype, 'calculateTop').and.callFake(function(scrollTop, topOffset) { spyOn(ScrollSpiedElement.prototype, 'calculateTop').and.callFake(
function(this: ScrollSpiedElement, scrollTop: number, topOffset: number) {
this.top = tops[this.index]; this.top = tops[this.index];
}); });
@ -234,7 +235,7 @@ describe('ScrollSpyService', () => {
it('should remember and emit the last active item to new subscribers', () => { it('should remember and emit the last active item to new subscribers', () => {
const items = [{index: 1}, {index: 2}, {index: 3}] as ScrollItem[]; const items = [{index: 1}, {index: 2}, {index: 3}] as ScrollItem[];
let lastActiveItem: ScrollItem | null; let lastActiveItem: ScrollItem|null;
const info = scrollSpyService.spyOn([]); const info = scrollSpyService.spyOn([]);
const spiedElemGroup = getSpiedElemGroups()[0]; const spiedElemGroup = getSpiedElemGroups()[0];
@ -246,12 +247,12 @@ describe('ScrollSpyService', () => {
spiedElemGroup.activeScrollItem.next(items[1]); spiedElemGroup.activeScrollItem.next(items[1]);
info.active.subscribe(item => lastActiveItem = item); info.active.subscribe(item => lastActiveItem = item);
expect(lastActiveItem).toBe(items[1]); expect(lastActiveItem!).toBe(items[1]);
spiedElemGroup.activeScrollItem.next(null); spiedElemGroup.activeScrollItem.next(null);
info.active.subscribe(item => lastActiveItem = item); info.active.subscribe(item => lastActiveItem = item);
expect(lastActiveItem).toBeNull(); expect(lastActiveItem!).toBeNull();
}); });
it('should only emit distinct values on `active`', () => { it('should only emit distinct values on `active`', () => {

View File

@ -102,7 +102,7 @@ export class ScrollSpiedElementGroup {
* @param {number} maxScrollTop - The maximum possible `scrollTop` (based on the viewport size). * @param {number} maxScrollTop - The maximum possible `scrollTop` (based on the viewport size).
*/ */
onScroll(scrollTop: number, maxScrollTop: number) { onScroll(scrollTop: number, maxScrollTop: number) {
let activeItem: ScrollItem; let activeItem: ScrollItem|undefined;
if (scrollTop + 1 >= maxScrollTop) { if (scrollTop + 1 >= maxScrollTop) {
activeItem = this.spiedElements[0]; activeItem = this.spiedElements[0];
@ -112,6 +112,7 @@ export class ScrollSpiedElementGroup {
activeItem = spiedElem; activeItem = spiedElem;
return true; return true;
} }
return false;
}); });
} }

View File

@ -111,7 +111,7 @@ describe('ScrollService', () => {
const topOfPage = new MockElement(); const topOfPage = new MockElement();
document.getElementById.and document.getElementById.and
.callFake(id => id === 'top-of-page' ? topOfPage : null); .callFake((id: string) => id === 'top-of-page' ? topOfPage : null);
scrollService.scroll(); scrollService.scroll();
expect(topOfPage.scrollIntoView).toHaveBeenCalled(); expect(topOfPage.scrollIntoView).toHaveBeenCalled();
@ -201,7 +201,7 @@ describe('ScrollService', () => {
it('should scroll to top', () => { it('should scroll to top', () => {
const topOfPageElement = <Element><any> new MockElement(); const topOfPageElement = <Element><any> new MockElement();
document.getElementById.and.callFake( document.getElementById.and.callFake(
id => id === 'top-of-page' ? topOfPageElement : null (id: string) => id === 'top-of-page' ? topOfPageElement : null
); );
scrollService.scrollToTop(); scrollService.scrollToTop();

View File

@ -20,7 +20,7 @@ export class ScrollService {
const toolbar = this.document.querySelector('.app-toolbar'); const toolbar = this.document.querySelector('.app-toolbar');
this._topOffset = (toolbar && toolbar.clientHeight || 0) + topMargin; this._topOffset = (toolbar && toolbar.clientHeight || 0) + topMargin;
} }
return this._topOffset; return this._topOffset!;
} }
get topOfPageElement() { get topOfPageElement() {
@ -54,7 +54,7 @@ export class ScrollService {
* Scroll to the element. * Scroll to the element.
* Don't scroll if no element. * Don't scroll if no element.
*/ */
scrollToElement(element: Element) { scrollToElement(element: Element|null) {
if (element) { if (element) {
element.scrollIntoView(); element.scrollIntoView();

View File

@ -30,8 +30,8 @@ describe('SearchResultsComponent', () => {
return take === undefined ? results : results.slice(0, take); return take === undefined ? results : results.slice(0, take);
} }
function compareTitle(l: {title: string}, r: {title: string}) { function compareTitle(l: SearchResult, r: SearchResult) {
return l.title.toUpperCase() > r.title.toUpperCase() ? 1 : -1; return l.title!.toUpperCase() > r.title!.toUpperCase() ? 1 : -1;
} }
function setSearchResults(query: string, results: SearchResult[]) { function setSearchResults(query: string, results: SearchResult[]) {
@ -117,7 +117,7 @@ describe('SearchResultsComponent', () => {
it('should omit search results with no title', () => { it('should omit search results with no title', () => {
const results = [ const results = [
{ path: 'news', title: undefined, type: 'marketing', keywords: '', titleWords: '' } { path: 'news', title: '', type: 'marketing', keywords: '', titleWords: '' }
]; ];
setSearchResults('something', results); setSearchResults('something', results);
@ -131,11 +131,11 @@ describe('SearchResultsComponent', () => {
describe('when a search result anchor is clicked', () => { describe('when a search result anchor is clicked', () => {
let searchResult: SearchResult; let searchResult: SearchResult;
let selected: SearchResult; let selected: SearchResult|null;
let anchor: DebugElement; let anchor: DebugElement;
beforeEach(() => { beforeEach(() => {
component.resultSelected.subscribe(result => selected = result); component.resultSelected.subscribe((result: SearchResult) => selected = result);
selected = null; selected = null;
searchResult = { path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' }; searchResult = { path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' };

View File

@ -44,7 +44,7 @@ export class SearchResultsComponent implements OnChanges {
return []; return [];
} }
this.notFoundMessage = 'No results found.'; this.notFoundMessage = 'No results found.';
const searchAreaMap = {}; const searchAreaMap: { [key: string]: SearchResult[] } = {};
search.results.forEach(result => { search.results.forEach(result => {
if (!result.title) { return; } // bad data; should fix if (!result.title) { return; } // bad data; should fix
const areaName = this.computeAreaName(result) || this.defaultArea; const areaName = this.computeAreaName(result) || this.defaultArea;

View File

@ -37,10 +37,10 @@ describe('SelectComponent', () => {
describe('button', () => { describe('button', () => {
it('should display the label if provided', () => { it('should display the label if provided', () => {
expect(getButton().textContent.trim()).toEqual(''); expect(getButton().textContent!.trim()).toEqual('');
host.label = 'Label:'; host.label = 'Label:';
fixture.detectChanges(); fixture.detectChanges();
expect(getButton().textContent.trim()).toEqual('Label:'); expect(getButton().textContent!.trim()).toEqual('Label:');
}); });
it('should contain a symbol `<span>` if hasSymbol is true', () => { it('should contain a symbol `<span>` if hasSymbol is true', () => {
@ -49,7 +49,7 @@ describe('SelectComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
const span = getButton().querySelector('span'); const span = getButton().querySelector('span');
expect(span).not.toEqual(null); expect(span).not.toEqual(null);
expect(span.className).toContain('symbol'); expect(span!.className).toContain('symbol');
}); });
it('should display the selected option, if there is one', () => { it('should display the selected option, if there is one', () => {
@ -57,7 +57,7 @@ describe('SelectComponent', () => {
host.selected = options[0]; host.selected = options[0];
fixture.detectChanges(); fixture.detectChanges();
expect(getButton().textContent).toContain(options[0].title); expect(getButton().textContent).toContain(options[0].title);
expect(getButton().querySelector('span').className).toContain(options[0].value); expect(getButton().querySelector('span')!.className).toContain(options[0].value);
}); });
it('should toggle the visibility of the options list when clicked', () => { it('should toggle the visibility of the options list when clicked', () => {
@ -90,7 +90,7 @@ describe('SelectComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 }); expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 });
expect(getButton().textContent).toContain(options[0].title); expect(getButton().textContent).toContain(options[0].title);
expect(getButton().querySelector('span').className).toContain(options[0].value); expect(getButton().querySelector('span')!.className).toContain(options[0].value);
}); });
it('should select the current option when enter is pressed', () => { it('should select the current option when enter is pressed', () => {
@ -99,7 +99,7 @@ describe('SelectComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 }); expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 });
expect(getButton().textContent).toContain(options[0].title); expect(getButton().textContent).toContain(options[0].title);
expect(getButton().querySelector('span').className).toContain(options[0].value); expect(getButton().querySelector('span')!.className).toContain(options[0].value);
}); });
it('should select the current option when space is pressed', () => { it('should select the current option when space is pressed', () => {
@ -108,7 +108,7 @@ describe('SelectComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 }); expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 });
expect(getButton().textContent).toContain(options[0].title); expect(getButton().textContent).toContain(options[0].title);
expect(getButton().querySelector('span').className).toContain(options[0].value); expect(getButton().querySelector('span')!.className).toContain(options[0].value);
}); });
it('should hide when an option is clicked', () => { it('should hide when an option is clicked', () => {
@ -155,7 +155,7 @@ function getButton(): HTMLButtonElement {
return element.query(By.css('button')).nativeElement; return element.query(By.css('button')).nativeElement;
} }
function getOptionContainer(): HTMLUListElement { function getOptionContainer(): HTMLUListElement|null {
const de = element.query(By.css('ul')); const de = element.query(By.css('ul'));
return de && de.nativeElement; return de && de.nativeElement;
} }

View File

@ -35,8 +35,8 @@ describe('TocService', () => {
it('should emit the latest value to new subscribers', () => { it('should emit the latest value to new subscribers', () => {
const expectedValue1 = tocItem('Heading A'); const expectedValue1 = tocItem('Heading A');
const expectedValue2 = tocItem('Heading B'); const expectedValue2 = tocItem('Heading B');
let value1: TocItem[]; let value1: TocItem[]|undefined;
let value2: TocItem[]; let value2: TocItem[]|undefined;
tocService.tocList.next([]); tocService.tocList.next([]);
tocService.tocList.subscribe(v => value1 = v); tocService.tocList.subscribe(v => value1 = v);
@ -235,22 +235,22 @@ describe('TocService', () => {
}); });
it('should have href with docId and heading\'s id', () => { it('should have href with docId and heading\'s id', () => {
const tocItem = lastTocList.find(item => item.title === 'Heading one'); const tocItem = lastTocList.find(item => item.title === 'Heading one')!;
expect(tocItem.href).toEqual(`${docId}#heading-one-special-id`); expect(tocItem.href).toEqual(`${docId}#heading-one-special-id`);
}); });
it('should have level "h1" for an <h1>', () => { it('should have level "h1" for an <h1>', () => {
const tocItem = lastTocList.find(item => item.title === 'Fun with TOC'); const tocItem = lastTocList.find(item => item.title === 'Fun with TOC')!;
expect(tocItem.level).toEqual('h1'); expect(tocItem.level).toEqual('h1');
}); });
it('should have level "h2" for an <h2>', () => { it('should have level "h2" for an <h2>', () => {
const tocItem = lastTocList.find(item => item.title === 'Heading one'); const tocItem = lastTocList.find(item => item.title === 'Heading one')!;
expect(tocItem.level).toEqual('h2'); expect(tocItem.level).toEqual('h2');
}); });
it('should have level "h3" for an <h3>', () => { it('should have level "h3" for an <h3>', () => {
const tocItem = lastTocList.find(item => item.title === 'H3 3a'); const tocItem = lastTocList.find(item => item.title === 'H3 3a')!;
expect(tocItem.level).toEqual('h3'); expect(tocItem.level).toEqual('h3');
}); });
@ -273,7 +273,7 @@ describe('TocService', () => {
}); });
it('should have href with docId and calculated heading id', () => { it('should have href with docId and calculated heading id', () => {
const tocItem = lastTocList.find(item => item.title === 'H2 Two'); const tocItem = lastTocList.find(item => item.title === 'H2 Two')!;
expect(tocItem.href).toEqual(`${docId}#h2-two`); expect(tocItem.href).toEqual(`${docId}#h2-two`);
}); });
@ -343,7 +343,7 @@ interface TestSafeHtml extends SafeHtml {
class TestDomSanitizer { class TestDomSanitizer {
bypassSecurityTrustHtml = jasmine.createSpy('bypassSecurityTrustHtml') bypassSecurityTrustHtml = jasmine.createSpy('bypassSecurityTrustHtml')
.and.callFake(html => { .and.callFake((html: string) => {
return { return {
changingThisBreaksApplicationSecurity: html, changingThisBreaksApplicationSecurity: html,
getTypeName: () => 'HTML', getTypeName: () => 'HTML',

View File

@ -37,7 +37,7 @@ export class TocService {
content: this.extractHeadingSafeHtml(heading), content: this.extractHeadingSafeHtml(heading),
href: `${docId}#${this.getId(heading, idMap)}`, href: `${docId}#${this.getId(heading, idMap)}`,
level: heading.tagName.toLowerCase(), level: heading.tagName.toLowerCase(),
title: heading.textContent.trim(), title: (heading.textContent || '').trim(),
})); }));
this.tocList.next(tocList); this.tocList.next(tocList);
@ -87,7 +87,7 @@ export class TocService {
if (id) { if (id) {
addToMap(id); addToMap(id);
} else { } else {
id = h.textContent.trim().toLowerCase().replace(/\W+/g, '-'); id = (h.textContent || '').trim().toLowerCase().replace(/\W+/g, '-');
id = addToMap(id); id = addToMap(id);
h.id = id; h.id = id;
} }
@ -95,7 +95,9 @@ export class TocService {
// Map guards against duplicate id creation. // Map guards against duplicate id creation.
function addToMap(key: string) { function addToMap(key: string) {
const count = idMap[key] = idMap[key] ? idMap[key] + 1 : 1; const oldCount = idMap.get(key) || 0;
const count = oldCount + 1;
idMap.set(key, count);
return count === 1 ? key : `${key}-${count}`; return count === 1 ? key : `${key}-${count}`;
} }
} }

View File

@ -31,7 +31,7 @@ describe('SwUpdatesService', () => {
checkInterval = (service as any).checkInterval; checkInterval = (service as any).checkInterval;
}; };
const tearDown = () => service.ngOnDestroy(); const tearDown = () => service.ngOnDestroy();
const run = specFn => () => { const run = (specFn: VoidFunction) => () => {
setup(); setup();
specFn(); specFn();
tearDown(); tearDown();
@ -90,7 +90,7 @@ describe('SwUpdatesService', () => {
}))); })));
it('should emit on `updateActivated` when an update has been activated', run(() => { it('should emit on `updateActivated` when an update has been activated', run(() => {
const activatedVersions: string[] = []; const activatedVersions: (string|undefined)[] = [];
service.updateActivated.subscribe(v => activatedVersions.push(v)); service.updateActivated.subscribe(v => activatedVersions.push(v));
sw.$$updatesSubj.next({type: 'pending', version: 'foo'}); sw.$$updatesSubj.next({type: 'pending', version: 'foo'});
@ -126,7 +126,7 @@ describe('SwUpdatesService', () => {
}))); })));
it('should stop emitting on `updateActivated`', run(() => { it('should stop emitting on `updateActivated`', run(() => {
const activatedVersions: string[] = []; const activatedVersions: (string|undefined)[] = [];
service.updateActivated.subscribe(v => activatedVersions.push(v)); service.updateActivated.subscribe(v => activatedVersions.push(v));
sw.$$updatesSubj.next({type: 'pending', version: 'foo'}); sw.$$updatesSubj.next({type: 'pending', version: 'foo'});

View File

@ -51,7 +51,7 @@ export class SwUpdatesService implements OnDestroy {
private activateUpdate() { private activateUpdate() {
this.log('Activating update...'); this.log('Activating update...');
this.sw.activateUpdate(null) this.sw.activateUpdate(null as any) // expects a non-null string
.subscribe(() => this.scheduleCheckForUpdate()); .subscribe(() => this.scheduleCheckForUpdate());
} }

View File

@ -38,7 +38,7 @@ export class TestDocViewerComponent extends DocViewerComponent {
template: '<aio-doc-viewer [doc]="currentDoc">Test Component</aio-doc-viewer>', template: '<aio-doc-viewer [doc]="currentDoc">Test Component</aio-doc-viewer>',
}) })
export class TestParentComponent { export class TestParentComponent {
currentDoc: DocumentContents; currentDoc?: DocumentContents|null;
@ViewChild(DocViewerComponent) docViewer: DocViewerComponent; @ViewChild(DocViewerComponent) docViewer: DocViewerComponent;
} }
@ -77,7 +77,7 @@ export class TestModule { }
export class ObservableWithSubscriptionSpies<T = void> extends Observable<T> { export class ObservableWithSubscriptionSpies<T = void> extends Observable<T> {
unsubscribeSpies: jasmine.Spy[] = []; unsubscribeSpies: jasmine.Spy[] = [];
subscribeSpy = spyOn(this, 'subscribe').and.callFake((...args) => { subscribeSpy = spyOn(this, 'subscribe').and.callFake((...args: any[]) => {
const subscription = super.subscribe(...args); const subscription = super.subscribe(...args);
const unsubscribeSpy = spyOn(subscription, 'unsubscribe').and.callThrough(); const unsubscribeSpy = spyOn(subscription, 'unsubscribe').and.callThrough();
this.unsubscribeSpies.push(unsubscribeSpy); this.unsubscribeSpies.push(unsubscribeSpy);

View File

@ -113,7 +113,7 @@ export class MockNgModuleFactoryLoader implements NgModuleFactoryLoader {
this.loadedPaths.push(path); this.loadedPaths.push(path);
const platformRef = getPlatform(); const platformRef = getPlatform();
const compilerFactory = platformRef.injector.get(CompilerFactory) as CompilerFactory; const compilerFactory = platformRef!.injector.get(CompilerFactory) as CompilerFactory;
const compiler = compilerFactory.createCompiler([]); const compiler = compilerFactory.createCompiler([]);
return compiler.compileModuleAsync(MockEmbeddedModule); return compiler.compileModuleAsync(MockEmbeddedModule);

View File

@ -4,7 +4,7 @@ export class MockLocationService {
urlSubject = new BehaviorSubject<string>(this.initialUrl); urlSubject = new BehaviorSubject<string>(this.initialUrl);
currentUrl = this.urlSubject.asObservable().map(url => this.stripSlashes(url)); currentUrl = this.urlSubject.asObservable().map(url => this.stripSlashes(url));
// strip off query and hash // strip off query and hash
currentPath = this.currentUrl.map(url => url.match(/[^?#]*/)[0]); currentPath = this.currentUrl.map(url => url.match(/[^?#]*/)![0]);
search = jasmine.createSpy('search').and.returnValue({}); search = jasmine.createSpy('search').and.returnValue({});
setSearch = jasmine.createSpy('setSearch'); setSearch = jasmine.createSpy('setSearch');
go = jasmine.createSpy('Location.go').and go = jasmine.createSpy('Location.go').and
@ -14,7 +14,7 @@ export class MockLocationService {
handleAnchorClick = jasmine.createSpy('Location.handleAnchorClick') handleAnchorClick = jasmine.createSpy('Location.handleAnchorClick')
.and.returnValue(false); // prevent click from causing a browser navigation .and.returnValue(false); // prevent click from causing a browser navigation
constructor(private initialUrl) {} constructor(private initialUrl: string) {}
private stripSlashes(url: string) { private stripSlashes(url: string) {
return url.replace(/^\/+/, '').replace(/\/+(\?|#|$)/, '$1'); return url.replace(/^\/+/, '').replace(/\/+(\?|#|$)/, '$1');

View File

@ -3,21 +3,21 @@ import { Injectable } from '@angular/core';
@Injectable() @Injectable()
export class MockLogger { export class MockLogger {
output = { output: { log: any[], error: any[], warn: any[] } = {
log: [], log: [],
error: [], error: [],
warn: [] warn: []
}; };
log(value: any, ...rest) { log(value: any, ...rest: any[]) {
this.output.log.push([value, ...rest]); this.output.log.push([value, ...rest]);
} }
error(value: any, ...rest) { error(value: any, ...rest: any[]) {
this.output.error.push([value, ...rest]); this.output.error.push([value, ...rest]);
} }
warn(value: any, ...rest) { warn(value: any, ...rest: any[]) {
this.output.warn.push([value, ...rest]); this.output.warn.push([value, ...rest]);
} }
} }

View File

@ -1,6 +1,8 @@
{ {
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"strict": true,
"noImplicitAny": false,
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"baseUrl": "src", "baseUrl": "src",
"sourceMap": true, "sourceMap": true,