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/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/service-worker": "^5.0.0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^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
// if `onlyDirect` is set to `true`.
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) {

View File

@ -62,6 +62,6 @@ export class SitePage {
getSearchResults() {
const results = element.all(by.css('.search-results li'));
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', () => {
setupSelectorForTesting();
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 });
expect(locationService.go).not.toHaveBeenCalled();
});
@ -520,9 +520,9 @@ describe('AppComponent', () => {
describe('aio-toc', () => {
let tocDebugElement: DebugElement;
let tocContainer: DebugElement;
let tocContainer: DebugElement|null;
const setHasFloatingToc = hasFloatingToc => {
const setHasFloatingToc = (hasFloatingToc: boolean) => {
component.hasFloatingToc = hasFloatingToc;
fixture.detectChanges();
@ -551,12 +551,12 @@ describe('AppComponent', () => {
});
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';
fixture.detectChanges();
expect(tocContainer.styles['max-height']).toBe('100px');
expect(tocContainer!.styles['max-height']).toBe('100px');
});
it('should restrain scrolling inside the ToC container', () => {
@ -565,7 +565,7 @@ describe('AppComponent', () => {
expect(restrainScrolling).not.toHaveBeenCalled();
tocContainer.triggerEventHandler('mousewheel', evt);
tocContainer!.triggerEventHandler('mousewheel', evt);
expect(restrainScrolling).toHaveBeenCalledWith(evt);
});
});
@ -591,7 +591,7 @@ describe('AppComponent', () => {
initializeTest();
fixture.detectChanges();
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');
});
function checkHostClass(type, value) {
function checkHostClass(type: string, value: string) {
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);
expect(classArray.length).toBeLessThanOrEqual(1, `"${classes}" should have only one class matching ${type}-*`);
expect(classArray).toEqual([`${type}-${value}`], `"${classes}" should contain ${type}-${value}`);
@ -1212,10 +1212,10 @@ class TestHttpClient {
if (/navigation\.json/.test(url)) {
data = this.navJson;
} else {
const match = /generated\/docs\/(.+)\.json/.exec(url);
const id = match[1];
const match = /generated\/docs\/(.+)\.json/.exec(url)!;
const id = match[1]!;
// 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 contents = `${h1}<h2 id="#somewhere">Some heading</h2>`;
data = { id, contents };

View File

@ -152,19 +152,19 @@ export class AppComponent implements OnInit {
this.navigationService.navigationViews.map(views => views['docVersions']))
.subscribe(([versionInfo, versions]) => {
// 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: 'stable', url: 'https://angular.io' },
];
if (this.deployment.mode === 'archive') {
computedVersions.push({ title: `v${versionInfo.major}`, url: null });
computedVersions.push({ title: `v${versionInfo.major}` });
}
this.docVersions = [...computedVersions, ...versions];
// Find the current version - eithers title matches the current deployment mode
// or its title matches the major version of the current version info
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})`;
});
@ -232,7 +232,7 @@ export class AppComponent implements OnInit {
}
@HostListener('window:resize', ['$event.target.innerWidth'])
onResize(width) {
onResize(width: number) {
this.isSideBySide = width > this.sideBySideWidth;
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)
let target = eventTarget;
let target: HTMLElement|null = eventTarget;
while (target && !(target instanceof HTMLAnchorElement)) {
target = target.parentElement;
}
@ -335,8 +335,8 @@ export class AppComponent implements OnInit {
// Must wait until now for mat-toolbar to be measurable.
const el = this.hostElement.nativeElement as Element;
this.tocMaxHeightOffset =
el.querySelector('footer').clientHeight +
el.querySelector('.app-toolbar').clientHeight +
el.querySelector('footer')!.clientHeight +
el.querySelector('.app-toolbar')!.clientHeight +
24; // fudge margin
}
@ -375,7 +375,7 @@ export class AppComponent implements OnInit {
}
}
doSearch(query) {
doSearch(query: string) {
this.searchResults = this.searchService.search(query);
this.showSearchResults = !!query;
}

View File

@ -23,7 +23,7 @@ describe('AppModule', () => {
});
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;
expect(eagerSelector).not.toBeNull();
@ -34,7 +34,7 @@ describe('AppModule', () => {
});
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;
expect(lazySelector).not.toBeNull();

View File

@ -2,5 +2,5 @@ export interface DocumentContents {
/** The unique identifier for this document */
id: string;
/** 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', () => {
let latestDocument: DocumentContents;
let latestDocument: DocumentContents|undefined;
const doc0 = { contents: 'doc 0', id: 'initial/doc' };
const doc1 = { contents: 'doc 1', id: 'new/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', () => {
let currentDocument: DocumentContents;
let currentDocument: DocumentContents|undefined;
const notFoundDoc = { id: FILE_NOT_FOUND_ID, contents: '<h1>Page Not Found</h1>' };
const { docService } = getServices('missing/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', () => {
let currentDocument: DocumentContents;
let currentDocument: DocumentContents|undefined;
const hardCodedNotFoundDoc = { contents: 'Document not found', id: FILE_NOT_FOUND_ID };
const nextDoc = { contents: 'Next Doc', id: 'new/doc' };
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)', () => {
let latestDocument: DocumentContents;
let latestDocument: DocumentContents|undefined;
const doc1 = { contents: 'doc 1' };
const doc2 = { contents: 'doc 2' };
const { docService, locationService } = getServices('initial/doc');
@ -107,7 +107,7 @@ describe('DocumentService', () => {
docService.currentDocument.subscribe(doc => latestDocument = doc);
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');
httpMock.expectOne({}).flush(doc1);
@ -119,14 +119,14 @@ describe('DocumentService', () => {
});
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 { docService, locationService } = getServices('initial/doc');
docService.currentDocument.subscribe(doc => latestDocument = doc);
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');
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', () => {
let latestDocument: DocumentContents;
let latestDocument: DocumentContents|undefined;
let subscription: Subscription;
const doc0 = { contents: 'doc 0' };

View File

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

View File

@ -127,9 +127,9 @@ describe('EmbedComponentsService', () => {
const componentRefs = service.createComponents(host);
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);
});

View File

@ -111,7 +111,7 @@ export class EmbedComponentsService {
// 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
// team and is considered to be safe.
host[contentPropertyName] = host.innerHTML;
(host as any)[contentPropertyName] = host.innerHTML;
componentRefs.push(factory.create(this.injector, [], host));
}
});
@ -141,7 +141,7 @@ export class EmbedComponentsService {
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) {
component.filteredSections.subscribe(filtered => {
let badItem: ApiItem;
let badItem: ApiItem|undefined;
expect(filtered.length).toBeGreaterThan(0, 'expected something');
expect(filtered.every(section => section.items.every(
item => {
@ -53,7 +53,7 @@ describe('ApiListComponent', () => {
});
it('should return all complete sections when no criteria', () => {
let filtered: ApiSection[];
let filtered: ApiSection[]|undefined;
component.filteredSections.subscribe(f => filtered = f);
expect(filtered).toEqual(sections);
});
@ -68,7 +68,7 @@ describe('ApiListComponent', () => {
component.filteredSections.subscribe(filtered => {
expect(filtered.length).toBe(1, 'only one section');
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;
service.sections.subscribe(
null,
null,
undefined,
undefined,
() => completed = true
);

View File

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

View File

@ -2,13 +2,13 @@
import { Component, ElementRef, OnInit } from '@angular/core';
export interface TabInfo {
class: string;
class: string|null;
code: string;
language: string;
language: string|null;
linenums: any;
path: 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`
// and make sure THAT runs after the tests by making component creation async
afterAll(() => {
delete window['prettyPrint'];
delete window['prettyPrintOne'];
delete (window as any)['prettyPrint'];
delete (window as any)['prettyPrintOne'];
});
beforeEach(() => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -94,7 +94,7 @@ export class LiveExampleComponent implements OnInit {
let exampleDir = attrs.name;
if (!exampleDir) {
// 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.zipName = exampleDir.indexOf('/') === -1 ? this.exampleDir : exampleDir.split('/')[0];
@ -150,7 +150,7 @@ export class LiveExampleComponent implements OnInit {
}
@HostListener('window:resize', ['$event.target.innerWidth'])
onResize(width) {
onResize(width: number) {
if (this.mode !== 'downloadOnly') {
this.calcPlnkrLink(width);
}

View File

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

View File

@ -43,7 +43,7 @@ describe('DocViewerComponent', () => {
describe('#doc', () => {
let renderSpy: jasmine.Spy;
const setCurrentDoc = (contents, id = 'fizz/buzz') => {
const setCurrentDoc = (contents: string|null, id = 'fizz/buzz') => {
parentComponent.currentDoc = {contents, id};
parentFixture.detectChanges();
};
@ -432,7 +432,7 @@ describe('DocViewerComponent', () => {
});
it('should store the embedded components', async () => {
const embeddedComponents = [];
const embeddedComponents: ComponentRef<any>[] = [];
embedIntoSpy.and.returnValue(of(embeddedComponents));
await doRender('Some content');
@ -678,7 +678,7 @@ describe('DocViewerComponent', () => {
describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => {
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);
});

View File

@ -114,12 +114,12 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
const hasToc = !!titleEl && !/no-?toc/i.test(titleEl.className);
if (hasToc) {
titleEl.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
}
return () => {
this.tocService.reset();
let title = '';
let title: string|null = '';
// Only create ToC for docs with an `<h1>` heading.
// 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
* 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 rafId = requestAnimationFrame(() => {
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),
// `time` values should be returned in seconds.
const getActualDuration = (elem: HTMLElement) => {
const cssValue = getComputedStyle(elem).transitionDuration;
const cssValue = getComputedStyle(elem).transitionDuration || '';
const seconds = Number(cssValue.replace(/s$/, ''));
return 1000 * seconds;
};
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
|| 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 = '';
return animationsDisabled
? this.void$.do(() => elem.style[prop] = to)
@ -201,7 +204,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
// setting each style.
.switchMap(() => raf$).do(() => elem.style[prop] = from)
.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$);
};
@ -214,7 +217,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
done$ = done$
// Remove the current view from the viewer.
.switchMap(() => animateLeave(this.currViewContainer))
.do(() => this.currViewContainer.parentElement.removeChild(this.currViewContainer))
.do(() => this.currViewContainer.parentElement!.removeChild(this.currViewContainer))
.do(() => this.docRemoved.emit());
}

View File

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

View File

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

View File

@ -97,7 +97,7 @@ export class NavigationService {
(navMap, 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);
currentNodes.connect();
@ -145,7 +145,10 @@ export class NavigationService {
if (url) {
// Strip off trailing slashes from nodes in the navMap - they are not relevant to matching
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 };
}

View File

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

View File

@ -8,29 +8,26 @@ describe('Attribute Utilities', () => {
beforeEach(() => {
const div = document.createElement('div');
div.innerHTML = `<div a b="true" c="false" D="foo" d-E></div>`;
testEl = div.querySelector('div');
testEl = div.querySelector('div')!;
});
describe('getAttrs', () => {
beforeEach(() => {
this.expectedMap = {
const expectedMap = {
a: '',
b: 'true',
c: 'false',
d: 'foo',
'd-e': ''
};
});
it('should get attr map from getAttrs(element)', () => {
const actual = getAttrs(testEl);
expect(actual).toEqual(this.expectedMap);
expect(actual).toEqual(expectedMap);
});
it('should get attr map from getAttrs(elementRef)', () => {
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 {
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 */) {
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 {
return attrs[typeof attr === 'string' ?
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 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
return attrValue == undefined ? def : attrValue.trim() !== 'false';
}

View File

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

View File

@ -11,7 +11,7 @@ describe('CustomIconRegistry', () => {
{ name: 'test_icon', svgSource: svgSrc }
];
const registry = new CustomIconRegistry(mockHttp, mockSanitizer, svgIcons);
let svgElement: SVGElement;
let svgElement: SVGElement|undefined;
registry.getNamedSvgIcon('test_icon').subscribe(el => svgElement = el);
expect(svgElement).toEqual(createSvg(svgSrc));
});
@ -36,8 +36,8 @@ describe('CustomIconRegistry', () => {
});
});
function createSvg(svgSrc) {
function createSvg(svgSrc: string): SVGElement {
const div = document.createElement('div');
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 => {
// SECURITY: the source for the SVG icons is provided in code by trusted developers
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');
}
ga(...args) {
this.window['ga'](...args);
ga(...args: any[]) {
(this.window as any)['ga'](...args);
}
}

View File

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

View File

@ -18,7 +18,7 @@ export class LocationService {
.map(url => this.stripSlashes(url));
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));
constructor(
@ -30,14 +30,14 @@ export class LocationService {
this.urlSubject.next(location.path(true));
this.location.subscribe(state => {
return this.urlSubject.next(state.url);
return this.urlSubject.next(state.url || '');
});
swUpdates.updateActivated.subscribe(() => this.swUpdateActivated = true);
}
// TODO?: ignore if url-without-hash-or-search matches current location?
go(url: string) {
go(url: string|null|undefined) {
if (!url) { return; }
url = this.stripSlashes(url);
if (/^http/.test(url) || this.swUpdateActivated) {
@ -62,8 +62,8 @@ export class LocationService {
return url.replace(/^\/+/, '').replace(/\/+(\?|#|$)/, '$1');
}
search(): { [index: string]: string; } {
const search = {};
search() {
const search: { [index: string]: string|undefined; } = {};
const path = this.location.path();
const q = path.indexOf('?');
if (q > -1) {
@ -80,11 +80,10 @@ export class LocationService {
return search;
}
setSearch(label: string, params: {}) {
setSearch(label: string, params: { [key: string]: string|undefined}) {
const search = Object.keys(params).reduce((acc, 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)}`;
}, '');

View File

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

View File

@ -60,14 +60,15 @@ describe('ScrollSpiedElementGroup', () => {
describe('#onScroll()', () => {
let group: ScrollSpiedElementGroup;
let activeItems: ScrollItem[];
let activeItems: (ScrollItem|null)[];
const activeIndices = () => activeItems.map(x => x && x.index);
beforeEach(() => {
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];
});
@ -246,12 +247,12 @@ describe('ScrollSpyService', () => {
spiedElemGroup.activeScrollItem.next(items[1]);
info.active.subscribe(item => lastActiveItem = item);
expect(lastActiveItem).toBe(items[1]);
expect(lastActiveItem!).toBe(items[1]);
spiedElemGroup.activeScrollItem.next(null);
info.active.subscribe(item => lastActiveItem = item);
expect(lastActiveItem).toBeNull();
expect(lastActiveItem!).toBeNull();
});
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).
*/
onScroll(scrollTop: number, maxScrollTop: number) {
let activeItem: ScrollItem;
let activeItem: ScrollItem|undefined;
if (scrollTop + 1 >= maxScrollTop) {
activeItem = this.spiedElements[0];
@ -112,6 +112,7 @@ export class ScrollSpiedElementGroup {
activeItem = spiedElem;
return true;
}
return false;
});
}

View File

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

View File

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

View File

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

View File

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

View File

@ -37,10 +37,10 @@ describe('SelectComponent', () => {
describe('button', () => {
it('should display the label if provided', () => {
expect(getButton().textContent.trim()).toEqual('');
expect(getButton().textContent!.trim()).toEqual('');
host.label = 'Label:';
fixture.detectChanges();
expect(getButton().textContent.trim()).toEqual('Label:');
expect(getButton().textContent!.trim()).toEqual('Label:');
});
it('should contain a symbol `<span>` if hasSymbol is true', () => {
@ -49,7 +49,7 @@ describe('SelectComponent', () => {
fixture.detectChanges();
const span = getButton().querySelector('span');
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', () => {
@ -57,7 +57,7 @@ describe('SelectComponent', () => {
host.selected = options[0];
fixture.detectChanges();
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', () => {
@ -90,7 +90,7 @@ describe('SelectComponent', () => {
fixture.detectChanges();
expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 });
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', () => {
@ -99,7 +99,7 @@ describe('SelectComponent', () => {
fixture.detectChanges();
expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 });
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', () => {
@ -108,7 +108,7 @@ describe('SelectComponent', () => {
fixture.detectChanges();
expect(host.onChange).toHaveBeenCalledWith({ option: options[0], index: 0 });
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', () => {
@ -155,7 +155,7 @@ function getButton(): HTMLButtonElement {
return element.query(By.css('button')).nativeElement;
}
function getOptionContainer(): HTMLUListElement {
function getOptionContainer(): HTMLUListElement|null {
const de = element.query(By.css('ul'));
return de && de.nativeElement;
}

View File

@ -35,8 +35,8 @@ describe('TocService', () => {
it('should emit the latest value to new subscribers', () => {
const expectedValue1 = tocItem('Heading A');
const expectedValue2 = tocItem('Heading B');
let value1: TocItem[];
let value2: TocItem[];
let value1: TocItem[]|undefined;
let value2: TocItem[]|undefined;
tocService.tocList.next([]);
tocService.tocList.subscribe(v => value1 = v);
@ -235,22 +235,22 @@ describe('TocService', () => {
});
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`);
});
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');
});
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');
});
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');
});
@ -273,7 +273,7 @@ describe('TocService', () => {
});
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`);
});
@ -343,7 +343,7 @@ interface TestSafeHtml extends SafeHtml {
class TestDomSanitizer {
bypassSecurityTrustHtml = jasmine.createSpy('bypassSecurityTrustHtml')
.and.callFake(html => {
.and.callFake((html: string) => {
return {
changingThisBreaksApplicationSecurity: html,
getTypeName: () => 'HTML',

View File

@ -37,7 +37,7 @@ export class TocService {
content: this.extractHeadingSafeHtml(heading),
href: `${docId}#${this.getId(heading, idMap)}`,
level: heading.tagName.toLowerCase(),
title: heading.textContent.trim(),
title: (heading.textContent || '').trim(),
}));
this.tocList.next(tocList);
@ -87,7 +87,7 @@ export class TocService {
if (id) {
addToMap(id);
} else {
id = h.textContent.trim().toLowerCase().replace(/\W+/g, '-');
id = (h.textContent || '').trim().toLowerCase().replace(/\W+/g, '-');
id = addToMap(id);
h.id = id;
}
@ -95,7 +95,9 @@ export class TocService {
// Map guards against duplicate id creation.
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}`;
}
}

View File

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

View File

@ -51,7 +51,7 @@ export class SwUpdatesService implements OnDestroy {
private activateUpdate() {
this.log('Activating update...');
this.sw.activateUpdate(null)
this.sw.activateUpdate(null as any) // expects a non-null string
.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>',
})
export class TestParentComponent {
currentDoc: DocumentContents;
currentDoc?: DocumentContents|null;
@ViewChild(DocViewerComponent) docViewer: DocViewerComponent;
}
@ -77,7 +77,7 @@ export class TestModule { }
export class ObservableWithSubscriptionSpies<T = void> extends Observable<T> {
unsubscribeSpies: jasmine.Spy[] = [];
subscribeSpy = spyOn(this, 'subscribe').and.callFake((...args) => {
subscribeSpy = spyOn(this, 'subscribe').and.callFake((...args: any[]) => {
const subscription = super.subscribe(...args);
const unsubscribeSpy = spyOn(subscription, 'unsubscribe').and.callThrough();
this.unsubscribeSpies.push(unsubscribeSpy);

View File

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

View File

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

View File

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

View File

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