Skip to content

Commit

Permalink
fix(typescript): Make UI-Router noImplicitAny safe.
Browse files Browse the repository at this point in the history
Closes #2693
  • Loading branch information
christopherthielen committed Aug 2, 2016
1 parent a854e89 commit 0769bc2
Show file tree
Hide file tree
Showing 52 changed files with 645 additions and 449 deletions.
124 changes: 73 additions & 51 deletions src/common/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import {isFunction, isString, isArray, isRegExp, isDate} from "./predicates";
import { all, any, not, prop, curry } from "./hof";
import {services} from "./coreservices";
import {State} from "../state/stateObject";

let w: any = typeof window === 'undefined' ? {} : window;
let angular = w.angular || {};
Expand All @@ -16,15 +17,17 @@ export const copy = angular.copy || _copy;
export const forEach = angular.forEach || _forEach;
export const extend = angular.extend || _extend;
export const equals = angular.equals || _equals;
export const identity = (x) => x;
export const noop = () => undefined;
export const identity = (x: any) => x;
export const noop = () => <any> undefined;

export type Mapper<X, T> = (x: X, key?: (string|number)) => T;
export interface TypedMap<T> { [key: string]: T; }
export type Predicate<X> = (X) => boolean;
export type Predicate<X> = (x: X) => boolean;
export type IInjectable = (Function|any[]);

export var abstractKey = 'abstract';
export interface Obj extends Object {
[key: string]: any
}

/**
* Binds and copies functions onto an object
Expand Down Expand Up @@ -81,7 +84,7 @@ export var abstractKey = 'abstract';
* @param bindTo The object which the functions will be bound to
* @param fnNames The function names which will be bound (Defaults to all the functions found on the 'from' object)
*/
export function bindFunctions(from, to, bindTo, fnNames: string[] = Object.keys(from)) {
export function bindFunctions(from: Obj, to: Obj, bindTo: Obj, fnNames: string[] = Object.keys(from)) {
return fnNames.filter(name => typeof from[name] === 'function')
.forEach(name => to[name] = from[name].bind(bindTo));
}
Expand All @@ -91,7 +94,7 @@ export function bindFunctions(from, to, bindTo, fnNames: string[] = Object.keys(
* prototypal inheritance helper.
* Creates a new object which has `parent` object as its prototype, and then copies the properties from `extra` onto it
*/
export const inherit = (parent, extra) =>
export const inherit = (parent: Obj, extra: Obj) =>
extend(new (extend(function() {}, { prototype: parent }))(), extra);

/**
Expand All @@ -117,13 +120,15 @@ export const inherit = (parent, extra) =>
* pick(obj, "foo", "bar"); // returns { foo: 1, bar: 2 }
* pick(obj, ["foo", "bar"]); // returns { foo: 1, bar: 2 }
*/
const restArgs = (args, idx = 0) => Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(args, idx));
const restArgs = (args: IArguments, idx = 0) =>
Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(args, idx));

/** Given an array, returns true if the object is found in the array, (using indexOf) */
export const inArray = (array: any[], obj: any) => array.indexOf(obj) !== -1;
export const inArray = (array: any[], obj: any) =>
array.indexOf(obj) !== -1;

/** Given an array, and an item, if the item is found in the array, it removes it (in-place). The same array is returned */
export const removeFrom = curry((array: any[], obj) => {
export const removeFrom = curry((array: any[], obj: any) => {
let idx = array.indexOf(obj);
if (idx >= 0) array.splice(idx, 1);
return array;
Expand All @@ -134,7 +139,7 @@ export const removeFrom = curry((array: any[], obj) => {
* to only those properties of the objects in the defaultsList.
* Earlier objects in the defaultsList take precedence when applying defaults.
*/
export function defaults(opts = {}, ...defaultsList) {
export function defaults(opts = {}, ...defaultsList: Obj[]) {
let defaults = merge.apply(null, [{}].concat(defaultsList));
return extend({}, defaults, pick(opts || {}, Object.keys(defaults)));
}
Expand All @@ -143,17 +148,17 @@ export function defaults(opts = {}, ...defaultsList) {
* Merges properties from the list of objects to the destination object.
* If a property already exists in the destination object, then it is not overwritten.
*/
export function merge(dst, ...objs: Object[]) {
forEach(objs, function(obj) {
forEach(obj, function(value, key) {
export function merge(dst: Obj, ...objs: Obj[]) {
forEach(objs, function(obj: Obj) {
forEach(obj, function(value: any, key: string) {
if (!dst.hasOwnProperty(key)) dst[key] = value;
});
});
return dst;
}

/** Reduce function that merges each element of the list into a single object, using extend */
export const mergeR = (memo, item) => extend(memo, item);
export const mergeR = (memo: Obj, item: Obj) => extend(memo, item);

/**
* Finds the common ancestor path between two states.
Expand All @@ -162,8 +167,8 @@ export const mergeR = (memo, item) => extend(memo, item);
* @param {Object} second The second state.
* @return {Array} Returns an array of state names in descending order, not including the root.
*/
export function ancestors(first, second) {
let path = [];
export function ancestors(first: State, second: State) {
let path: State[] = [];

for (var n in first.path) {
if (first.path[n] !== second.path[n]) break;
Expand All @@ -181,18 +186,18 @@ export function ancestors(first, second) {
* it defaults to the list of keys in `a`.
* @return {Boolean} Returns `true` if the keys match, otherwise `false`.
*/
export function equalForKeys(a, b, keys: string[] = Object.keys(a)) {
export function equalForKeys(a: Obj, b: Obj, keys: string[] = Object.keys(a)) {
for (var i = 0; i < keys.length; i++) {
let k = keys[i];
if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
}
return true;
}

type PickOmitPredicate = (keys: string[], key) => boolean;
function pickOmitImpl(predicate: PickOmitPredicate, obj) {
let objCopy = {}, keys = restArgs(arguments, 2);
for (var key in obj) {
type PickOmitPredicate = (keys: string[], key: string) => boolean;
function pickOmitImpl(predicate: PickOmitPredicate, obj: Obj, ...keys: string[]) {
let objCopy = {};
for (let key in obj) {
if (predicate(keys, key)) objCopy[key] = obj[key];
}
return objCopy;
Expand All @@ -208,7 +213,7 @@ function pickOmitImpl(predicate: PickOmitPredicate, obj) {
* @param obj the source object
* @param propNames an Array of strings, which are the whitelisted property names
*/
export function pick(obj, propNames: string[]): Object;
export function pick(obj: Obj, propNames: string[]): Obj;
/**
* @example
* ```
Expand All @@ -219,9 +224,11 @@ export function pick(obj, propNames: string[]): Object;
* @param obj the source object
* @param propNames 1..n strings, which are the whitelisted property names
*/
export function pick(obj, ...propNames: string[]): Object;
export function pick(obj: Obj, ...propNames: string[]): Obj;
/** Return a copy of the object only containing the whitelisted properties. */
export function pick(obj) { return pickOmitImpl.apply(null, [inArray].concat(restArgs(arguments))); }
export function pick(obj: Obj) {
return pickOmitImpl.apply(null, [inArray].concat(restArgs(arguments)));
}

/**
* @example
Expand All @@ -233,7 +240,7 @@ export function pick(obj) { return pickOmitImpl.apply(null, [inArray].concat(res
* @param obj the source object
* @param propNames an Array of strings, which are the blacklisted property names
*/
export function omit(obj, propNames: string[]): Object;
export function omit(obj: Obj, propNames: string[]): Obj;
/**
* @example
* ```
Expand All @@ -244,29 +251,32 @@ export function omit(obj, propNames: string[]): Object;
* @param obj the source object
* @param propNames 1..n strings, which are the blacklisted property names
*/
export function omit(obj, ...propNames: string[]): Object;
export function omit(obj: Obj, ...propNames: string[]): Obj;
/** Return a copy of the object omitting the blacklisted properties. */
export function omit(obj) { return pickOmitImpl.apply(null, [not(inArray)].concat(restArgs(arguments))); }
export function omit(obj: Obj) {
let notInArray = (array, item) => !inArray(array, item);
return pickOmitImpl.apply(null, [notInArray].concat(restArgs(arguments)));
}


/** Given an array of objects, maps each element to a named property of the element. */
export function pluck(collection: any[], propName: string): any[];
export function pluck(collection: Obj[], propName: string): Obj[];
/** Given an object, maps each property of the object to a named property of the property. */
export function pluck(collection: { [key: string]: any }, propName: string): { [key: string]: any };
/**
* Maps an array, or object to a property (by name)
*/
export function pluck(collection, propName): any {
export function pluck(collection: any, propName: string): any {
return map(collection, <Mapper<any, string>> prop(propName));
}


/** Given an array of objects, returns a new array containing only the elements which passed the callback predicate */
export function filter<T>(collection: T[], callback: (T, key?) => boolean): T[];
export function filter<T>(collection: T[], callback: (t: T, key?: number) => boolean): T[];
/** Given an object, returns a new object with only those properties that passed the callback predicate */
export function filter<T>(collection: TypedMap<T>, callback: (T, key?) => boolean): TypedMap<T>;
export function filter<T>(collection: TypedMap<T>, callback: (t: T, key?: string) => boolean): TypedMap<T>;
/** Filters an Array or an Object's properties based on a predicate */
export function filter<T>(collection: T, callback: Function): T {
export function filter<T>(collection: any, callback: Function): T {
let arr = isArray(collection), result: any = arr ? [] : {};
let accept = arr ? x => result.push(x) : (x, key) => result[key] = x;
forEach(collection, function(item, i) {
Expand All @@ -281,7 +291,7 @@ export function find<T>(collection: TypedMap<T>, callback: Predicate<T>): T;
/** Given an array of objects, returns the first object which passed the callback predicate */
export function find<T>(collection: T[], callback: Predicate<T>): T;
/** Finds an object from an array, or a property of an object, that matches a predicate */
export function find(collection, callback) {
export function find(collection: any, callback: any) {
let result;

forEach(collection, function(item, i) {
Expand Down Expand Up @@ -314,7 +324,8 @@ export function map(collection: any, callback: any): any {
* let vals = values(foo); // [ 1, 2, 3 ]
* ```
*/
export const values: (<T> (obj: TypedMap<T>) => T[]) = (obj) => Object.keys(obj).map(key => obj[key]);
export const values: (<T> (obj: TypedMap<T>) => T[]) = (obj: Obj) =>
Object.keys(obj).map(key => obj[key]);

/**
* Reduce function that returns true if all of the values are truthy.
Expand All @@ -329,7 +340,7 @@ export const values: (<T> (obj: TypedMap<T>) => T[]) = (obj) => Object.keys(obj)
* vals.reduce(allTrueR, true); // false
* ```
*/
export const allTrueR = (memo: boolean, elem) => memo && elem;
export const allTrueR = (memo: boolean, elem: any) => memo && elem;

/**
* Reduce function that returns true if any of the values are truthy.
Expand All @@ -344,7 +355,7 @@ export const allTrueR = (memo: boolean, elem) => memo && elem;
* vals.reduce(anyTrueR, true); // true
* ```
*/
export const anyTrueR = (memo: boolean, elem) => memo || elem;
export const anyTrueR = (memo: boolean, elem: any) => memo || elem;

/**
* Reduce function which un-nests a single level of arrays
Expand All @@ -355,7 +366,7 @@ export const anyTrueR = (memo: boolean, elem) => memo || elem;
* input.reduce(unnestR, []) // [ "a", "b", "c", "d", [ "double, "nested" ] ]
* ```
*/
export const unnestR = (memo: any[], elem) => memo.concat(elem);
export const unnestR = (memo: any[], elem: any[]) => memo.concat(elem);

/**
* Reduce function which recursively un-nests all arrays
Expand All @@ -367,12 +378,18 @@ export const unnestR = (memo: any[], elem) => memo.concat(elem);
* input.reduce(unnestR, []) // [ "a", "b", "c", "d", "double, "nested" ]
* ```
*/
export const flattenR = (memo: any[], elem) => isArray(elem) ? memo.concat(elem.reduce(flattenR, [])) : pushR(memo, elem);
export const flattenR = (memo: any[], elem: any) =>
isArray(elem) ? memo.concat(elem.reduce(flattenR, [])) : pushR(memo, elem);

/** Reduce function that pushes an object to an array, then returns the array. Mostly just for [[flattenR]] */
export function pushR(arr: any[], obj) { arr.push(obj); return arr; }
export function pushR(arr: any[], obj: any) {
arr.push(obj);
return arr;
}

/** Reduce function that filters out duplicates */
export const uniqR = (acc, token) => inArray(acc, token) ? acc : pushR(acc, token);
export const uniqR = (acc: any[], token: any) =>
inArray(acc, token) ? acc : pushR(acc, token);

/**
* Return a new array with a single level of arrays unnested.
Expand Down Expand Up @@ -428,7 +445,8 @@ export function assertPredicate<T>(predicate: Predicate<T>, errMsg: (string|Func
* pairs({ foo: "FOO", bar: "BAR }) // [ [ "foo", "FOO" ], [ "bar": "BAR" ] ]
* ```
*/
export const pairs = (object) => Object.keys(object).map(key => [ key, object[key]] );
export const pairs = (obj: Obj) =>
Object.keys(obj).map(key => [ key, obj[key]] );

/**
* Given two or more parallel arrays, returns an array of tuples where
Expand Down Expand Up @@ -471,7 +489,7 @@ export function arrayTuples(...arrayArgs: any[]): any[] {
* ```
*/
export function applyPairs(memo: TypedMap<any>, keyValTuple: any[]) {
let key, value;
let key: string, value: any;
if (isArray(keyValTuple)) [key, value] = keyValTuple;
if (!isString(key)) throw new Error("invalid parameters to applyPairs");
memo[key] = value;
Expand All @@ -489,25 +507,29 @@ export function tail<T>(arr: T[]): T {
* note: This is a shallow copy, while angular.copy is a deep copy.
* ui-router uses `copy` only to make copies of state parameters.
*/
function _copy(src, dest) {
function _copy(src: Obj, dest: Obj) {
if (dest) Object.keys(dest).forEach(key => delete dest[key]);
if (!dest) dest = {};
return extend(dest, src);
}

function _forEach(obj: (any[]|any), cb, _this) {
/** Naive forEach implementation works with Objects or Arrays */
function _forEach(obj: (any[]|any), cb: Function, _this: Obj) {
if (isArray(obj)) return obj.forEach(cb, _this);
Object.keys(obj).forEach(key => cb(obj[key], key));
}

function _copyProps(to, from) { Object.keys(from).forEach(key => to[key] = from[key]); return to; }
function _extend(toObj, fromObj);
function _extend(toObj, ...fromObj);
function _extend(toObj, rest) {
function _copyProps(to: Obj, from: Obj) {
Object.keys(from).forEach(key => to[key] = from[key]);
return to;
}
function _extend(toObj: Obj, fromObj: Obj): Obj;
function _extend(toObj: Obj, ...fromObj: Obj[]): Obj;
function _extend(toObj: Obj) {
return restArgs(arguments, 1).filter(identity).reduce(_copyProps, toObj);
}

function _equals(o1, o2) {
function _equals(o1: any, o2: any): boolean {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
Expand All @@ -523,7 +545,7 @@ function _equals(o1, o2) {
let predicates = [isFunction, isArray, isDate, isRegExp];
if (predicates.map(any).reduce((b, fn) => b || !!fn(tup), false)) return false;

let key, keys = {};
let key: string, keys: { [i: string]: boolean } = {};
for (key in o1) {
if (!_equals(o1[key], o2[key])) return false;
keys[key] = true;
Expand All @@ -535,7 +557,7 @@ function _equals(o1, o2) {
return true;
}

function _arraysEq(a1, a2) {
function _arraysEq(a1: any[], a2: any[]) {
if (a1.length !== a2.length) return false;
return arrayTuples(a1, a2).reduce((b, t) => b && _equals(t[0], t[1]), true);
}
Expand Down
Loading

0 comments on commit 0769bc2

Please sign in to comment.