Skip to content

Commit

Permalink
refactor(typescript): support strict null checking
Browse files Browse the repository at this point in the history
* New `ColumnFieldContextPartial` type
* New `GroupingPartial` type
  • Loading branch information
ccrowhurstram committed Dec 22, 2016
1 parent 8f5461f commit cac6e50
Show file tree
Hide file tree
Showing 20 changed files with 178 additions and 153 deletions.
2 changes: 1 addition & 1 deletion src/browser/ngTableController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export class NgTableController<TParams, TCol extends ColumnDefPartial | DynamicT
pagination: (this.$attrs.templatePagination ? this.$attrs.templatePagination : 'ng-table/pager.html')
};
this.$element.addClass('ng-table');
let headerTemplate: IAugmentedJQuery = null;
let headerTemplate: IAugmentedJQuery | undefined;

// $element.find('> thead').length === 0 doesn't work on jqlite
let theadFound = false;
Expand Down
2 changes: 1 addition & 1 deletion src/browser/ngTableGroupRowController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class NgTableGroupRowController<T> {
if (this.isGroupingFunc(grouping)) {
this.groupFns = [grouping];
this.$scope.$selGroup = grouping;
this.$scope.$selGroupTitle = grouping.title;
this.$scope.$selGroupTitle = grouping.title || '';
} else {
// note: currently only one group is implemented
const groupKey = Object.keys(grouping || {})[0];
Expand Down
24 changes: 13 additions & 11 deletions src/browser/ngTableSelectFilterDs.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
* @license New BSD License <http://creativecommons.org/licenses/BSD/>
*/

import * as ng1 from 'angular';
import { IAttributes, IDirective, IParseService, IQService, IPromise, IScope } from 'angular';
import { ColumnDef, SelectData, SelectDataFunc, SelectOption } from './public-interfaces';

/**
* @private
*/
export interface InputAttributes extends ng1.IAttributes {
export interface InputAttributes extends IAttributes {
ngTableSelectFilterDs: string;
}

Expand All @@ -33,7 +33,7 @@ ngTableSelectFilterDs.$inject = [];
*
* This directive is is focused on providing a datasource to an `ngOptions` directive
*/
function ngTableSelectFilterDs(): ng1.IDirective {
function ngTableSelectFilterDs(): IDirective {
// note: not using isolated or child scope "by design"
// this is to allow this directive to be combined with other directives that do

Expand All @@ -51,13 +51,13 @@ export class NgTableSelectFilterDsController {
static $inject = ['$scope', '$parse', '$attrs', '$q'];
$column: ColumnDef;
constructor(
private $scope: ng1.IScope & ScopeExtensions,
$parse: ng1.IParseService,
private $scope: IScope & ScopeExtensions,
$parse: IParseService,
private $attrs: InputAttributes,
private $q: ng1.IQService) {
private $q: IQService) {

this.$column = $parse($attrs.ngTableSelectFilterDs)($scope);
$scope.$watch<SelectData>(
$scope.$watch<SelectData | undefined>(
() => this.$column && this.$column.data,
() => { this.bindDataSource(); });
}
Expand All @@ -72,7 +72,7 @@ export class NgTableSelectFilterDsController {
}

private hasEmptyOption(data: SelectOption[]) {
let isMatch: boolean;
let isMatch = false;
for (let i = 0; i < data.length; i++) {
const item = data[i];
if (item && item.id === '') {
Expand All @@ -85,11 +85,13 @@ export class NgTableSelectFilterDsController {

private getSelectListData($column: ColumnDef) {
const dataInput = $column.data;
if (dataInput instanceof Array) {
return this.$q.when(dataInput);
let result: IPromise<SelectOption[]> | SelectOption[] | undefined;
if (typeof dataInput === 'function') {
result = dataInput();
} else {
return this.$q.when(dataInput && dataInput());
result = dataInput;
}
return this.$q.when<SelectOption[] | undefined>(result);
}
}
export { ngTableSelectFilterDs };
11 changes: 8 additions & 3 deletions src/browser/public-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import { IAttributes, IPromise, IScope } from 'angular';
* The scope available to a table column getter
*/
export type ColumnFieldContext = IScope & {
$column: ColumnDef;
$columns: ColumnDef[];
}

export type ColumnFieldContextPartial = IScope & {
$column?: ColumnDef;
$columns: ColumnDef[];
}
Expand All @@ -12,7 +17,7 @@ export type ColumnFieldContext = IScope & {
* Signature of a getter/setter on a {@link IColumnDef} instance
*/
export interface ColumnField<T> {
(context?: ColumnFieldContext): T;
(context?: ColumnFieldContextPartial): T;
(value: T): void;
assign($scope: IScope, value: T): void;
}
Expand Down Expand Up @@ -102,7 +107,7 @@ export type DynamicTableColField<T> = DynamicTableColFieldFunc<T> | T;
* Signature of a getter/setter on a {@link IDynamicTableColDef} instance
*/
export interface DynamicTableColFieldFunc<T> {
(context: ColumnFieldContext): T;
(context: ColumnFieldContextPartial): T;
}

/**
Expand Down Expand Up @@ -207,7 +212,7 @@ export interface SelectOption {
* Signature of a function that will return the options that will be rendered by a html select
*/
export interface SelectDataFunc {
(): SelectOption[] | IPromise<SelectOption[]>
(): SelectOption[] | IPromise<SelectOption[]> | undefined
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/core/data/ngTableDefaultGetData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { NgTableEventsChannel } from '../ngTableEventsChannel';
* - assign the total item count after filtering to the `total` of the `NgTableParams` instance supplied
*/
export interface DefaultGetData<T> {
(data: T[], params: NgTableParams<T>): T[];
(data: T[] | undefined, params: NgTableParams<T>): T[];
/**
* Convenience function that this service will use to apply paging to the data rows.
*
Expand Down
5 changes: 4 additions & 1 deletion src/core/grouping/groupingFunc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { GroupSort } from './groupSettings';

export type Grouping<T> = GroupValues | GroupingFunc<T>;
export type GroupingPartial<T> = GroupValuesPartial | GroupingFunc<T>;

/**
* Signature of a function that should return the name of the group
Expand All @@ -18,4 +19,6 @@ export interface GroupingFunc<T> {
/**
* Map of the names of fields on a data row and the corrosponding sort direction
*/
export interface GroupValues { [name: string]: GroupSort }
export interface GroupValues { [name: string]: GroupSort }

export type GroupValuesPartial = Partial<GroupValues>;
2 changes: 1 addition & 1 deletion src/core/grouping/ngTableDefaultGetGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function ngTableDefaultGetGroups<T>($q: IQService, ngTableDefaultGetData:

const group = params.group();
let groupFn: GroupingFunc<T>;
let sortDirection: GroupSort = undefined;
let sortDirection: GroupSort | undefined;
if (isGroupingFun(group)) {
groupFn = group;
sortDirection = group.sortDirection;
Expand Down
8 changes: 3 additions & 5 deletions src/core/ngTableEventsChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export interface NgTableEventsChannel {

publishAfterCreated<T>(publisher: NgTableParams<T>): void;
publishAfterReloadData<T>(publisher: NgTableParams<T>, newData: T[], oldData: T[]): void;
publishDatasetChanged<T>(publisher: NgTableParams<T>, newDataset: T[], oldDataset: T[]): void;
publishDatasetChanged<T>(publisher: NgTableParams<T>, newDataset: T[] | undefined, oldDataset: T[] | undefined): void;
publishPagesChanged<T>(publisher: NgTableParams<T>, newPages: PageButton[], oldPages: PageButton[]): void;
publishAfterDataFiltered<T>(publisher: NgTableParams<T>, newData: T[]): void;
publishAfterDataSorted<T>(params: NgTableParams<T>, newData: T[]): void;
Expand Down Expand Up @@ -263,10 +263,8 @@ export class NgTableEventsChannel {
});
}

function createEventSelectorFn<T>(eventSelector: EventSelector<T>): (publisher: NgTableParams<any>) => boolean {
if (!eventSelector) {
return (publisher: NgTableParams<any>) => true;
} else if (isEventSelectorFunc(eventSelector)) {
function createEventSelectorFn<T>(eventSelector: EventSelector<T> = () => true): EventSelectorFunc {
if (isEventSelectorFunc(eventSelector)) {
return eventSelector
} else {
// shorthand for subscriber to only receive events from a specific publisher instance
Expand Down
76 changes: 39 additions & 37 deletions src/core/ngTableParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { NgTableEventsChannel } from './ngTableEventsChannel'
import { SettingsPartial, Settings } from './ngTableSettings'
import { DataResult, DataRowGroup, GetDataFunc } from './data';
import { FilterValues } from './filtering';
import { GetGroupFunc, Grouping, GroupingFunc, GroupSort, GroupValues } from './grouping';
import { GetGroupFunc, Grouping, GroupingPartial, GroupValuesPartial, GroupingFunc, GroupSort, GroupValues } from './grouping';
import { SortDirection, SortingValues } from './sorting';
import { PageButton } from './paging';

Expand All @@ -27,7 +27,11 @@ export interface InternalTableParams<T> extends NgTableParams<T> {
}


export type ParamValuesPartial<T> = Partial<ParamValues<T>>;
export type ParamValuesPartial<T> =
Partial<Pick<ParamValues<T>, 'page' | 'count' | 'filter' | 'sorting'>>
& {
group?: string | GroupingPartial<T>
};

/**
* The runtime values for {@link NgTableParams} that determine the set of data rows and
Expand Down Expand Up @@ -82,18 +86,18 @@ export class NgTableParams<T> {
data: T[] = [];
reloadPages: () => void;
private defaultSettings = Settings.createWithOverrides<T>();
private errParamsMemento: Memento<T>;
private errParamsMemento: Memento<T> | null;
private isCommittedDataset = false;
isNullInstance: boolean;
private initialEvents: Function[] = [];
private initialEvents: Function[] | null = [];
private ngTableDefaults: Defaults
private ngTableEventsChannel: NgTableEventsChannel;
private prevParamsMemento: Memento<T>;
private _params = new ParamValues();
private _params = new ParamValues<T>();
private _settings = this.defaultSettings;
private $q: IQService;
private $log: ILogService
constructor(baseParameters?: ParamValuesPartial<T> | boolean, baseSettings?: SettingsPartial<T>) {
constructor(baseParameters: ParamValuesPartial<T> | boolean = {}, baseSettings: SettingsPartial<T> = {}) {

// the ngTableController "needs" to create a dummy/null instance and it's important to know whether an instance
// is one of these
Expand Down Expand Up @@ -249,7 +253,7 @@ export class NgTableParams<T> {
* Sets grouping to the `group` supplied; any existing grouping will be removed.
* Changes to group will cause `isDataReloadRequired` to return true and the current `page` to be set to 1
*/
group(group: GroupValues): this
group(group: GroupValuesPartial): this
/**
* Sets grouping to the `field` and `sortDirection` supplied; any existing grouping will be removed
* Changes to group will cause `isDataReloadRequired` to return true and the current `page` to be set to 1
Expand All @@ -261,7 +265,7 @@ export class NgTableParams<T> {
* Changes to group will cause `isDataReloadRequired` to return true and the current `page` to be set to 1
*/
group(group: GroupingFunc<T> | string, sortDirection?: GroupSort): this
group(group?: Grouping<T> | string, sortDirection?: GroupSort): string | Grouping<T> | this {
group(group?: GroupingPartial<T> | string, sortDirection?: GroupSort): string | Grouping<T> | this {
if (group === undefined) {
return this._params.group;
}
Expand Down Expand Up @@ -387,7 +391,7 @@ export class NgTableParams<T> {
}

// todo: move parsing of url like parameters into a seperate method

parseParamsFromUrl = parseParamsFromUrl || false;
for (const key in newParameters) {
let value = newParameters[key];
Expand Down Expand Up @@ -424,7 +428,7 @@ export class NgTableParams<T> {
* Trigger a reload of the data rows
*/
reload<TResult extends DataResult<T>>(): IPromise<TResult[]> {
let pData: ng1.IPromise<any> = null;
let pData: ng1.IPromise<any>;

this._settings.$loading = true;

Expand Down Expand Up @@ -469,35 +473,35 @@ export class NgTableParams<T> {
*/
settings(newSettings: SettingsPartial<T>): this
settings(newSettings?: SettingsPartial<T>): this | Settings<T> {
if (ng1.isDefined(newSettings)) {
if (newSettings === undefined) {
return this._settings;
}

const settings = Settings.merge(this._settings, newSettings);
const settings = Settings.merge(this._settings, newSettings);

const originalDataset = this._settings.dataset;
this._settings = settings;
const originalDataset = this._settings.dataset;
this._settings = settings;

// note: using != as want null and undefined to be treated the same
const hasDatasetChanged = newSettings.hasOwnProperty('dataset') && (newSettings.dataset != originalDataset);
if (hasDatasetChanged) {
if (this.isCommittedDataset) {
this.page(1); // reset page as a new dataset has been supplied
}
this.isCommittedDataset = false;
// note: using != as want null and undefined to be treated the same
const hasDatasetChanged = newSettings.hasOwnProperty('dataset') && (newSettings.dataset != originalDataset);
if (hasDatasetChanged) {
if (this.isCommittedDataset) {
this.page(1); // reset page as a new dataset has been supplied
}
this.isCommittedDataset = false;

const fireEvent = () => {
this.ngTableEventsChannel.publishDatasetChanged(this, newSettings.dataset, originalDataset);
};
const fireEvent = () => {
this.ngTableEventsChannel.publishDatasetChanged(this, newSettings.dataset, originalDataset);
};

if (this.initialEvents) {
this.initialEvents.push(fireEvent);
} else {
fireEvent();
}
if (this.initialEvents) {
this.initialEvents.push(fireEvent);
} else {
fireEvent();
}
this.log('ngTable: set settings', this._settings);
return this;
}
return this._settings;
this.log('ngTable: set settings', this._settings);
return this;
}
/**
* Returns the current sorting used to order the data rows.
Expand All @@ -512,11 +516,11 @@ export class NgTableParams<T> {
/**
* Sets sorting to the `field` and `direction` supplied; any existing sorting will be removed
*/
sorting(field: string, direction: string): this
sorting(field: string, direction?: string): this
sorting(sorting?: SortingValues | string, direction?: SortDirection) {
if (typeof sorting === 'string') {
this.parameters({
'sorting': { [sorting]: direction }
'sorting': { [sorting]: direction || this.settings().defaultSort }
});
return this;
}
Expand Down Expand Up @@ -553,9 +557,7 @@ export class NgTableParams<T> {
* true for the parameters to be returned as an array of strings of the form 'paramName=value'
* otherwise parameters returned as a key-value object
*/
url(asString?: boolean) {
// this function is an example of Typescript gone bad!!
asString = asString || false;
url(asString = false) {
const pairs: any[] | { [name: string]: string } = (asString ? [] : {});
for (const key in this._params) {
if (this._params.hasOwnProperty(key)) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/ngTableSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class Settings<T> {
private static instance: Settings<any>;
static createWithOverrides<T>(): Settings<T> {
checkClassInit(Settings);
return Settings.merge(Settings.instance, Settings.ngTableDefaults.settings);
return Settings.merge(Settings.instance, Settings.ngTableDefaults.settings || {});
}
static merge<T>(existing: Settings<T>, newSettings: SettingsPartial<T>): Settings<T> {
checkClassInit(Settings);
Expand Down
4 changes: 2 additions & 2 deletions src/core/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Grouping, GroupingFunc } from './grouping';
import { GroupingPartial, GroupingFunc } from './grouping';
import { SortingValues } from './sorting';

/**
Expand All @@ -15,6 +15,6 @@ export function convertSortToOrderBy(sorting: SortingValues) {
/**
* @private
*/
export function isGroupingFun(val: string | Grouping<any>): val is GroupingFunc<any> {
export function isGroupingFun(val: string | GroupingPartial<any>): val is GroupingFunc<any> {
return typeof val === 'function'
}
8 changes: 8 additions & 0 deletions test/custom-typings/jasmine-type-fixes.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare namespace jasmine {

// todo: remove once fixed (see: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/12801)
function addCustomEqualityTester(equalityTester: CustomEqualityTesterFixed): void;
export interface CustomEqualityTesterFixed {
(first: any, second: any): boolean | undefined;
}
}
4 changes: 3 additions & 1 deletion test/specs/filters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ describe('ngTableFilterConfig', () => {
}
});

// cheating the compiler here and allowing an undefined value
// to be supplied (oridinarily this would be disallowed by the compiler)
ngTableFilterConfigProvider.setConfig({
aliasUrls: {
'text': undefined
'text': undefined as any
}
});

Expand Down
Loading

0 comments on commit cac6e50

Please sign in to comment.