@type-r/mixture
Version:
React-style mixins, Backbone-style events, logging router.
290 lines (234 loc) • 7.72 kB
text/typescript
/** Similar to underscore `_.defaults` */
export function defaults< T >( dest : T, ...sources : Object[] ) : T
export function defaults< T >( dest : T, source : Object ) : T {
for( var name in source ) {
if( source.hasOwnProperty( name ) && !dest.hasOwnProperty( name ) ) {
dest[ name ] = source[ name ];
}
}
if( arguments.length > 2 ){
for( let i = 2; i < arguments.length; i++ ){
const other = arguments[ i ];
other && defaults( dest, other );
}
}
return dest;
}
/** Check if value is raw JSON */
export function isValidJSON( value : any ) : boolean {
if( value === null ){
return true;
}
switch( typeof value ){
case 'number' :
case 'string' :
case 'boolean' :
return true;
case 'object':
var proto = Object.getPrototypeOf( value );
if( proto === Object.prototype || proto === Array.prototype ){
return every( value, isValidJSON );
}
}
return false;
}
/** Get the base class constructor function.
* @param Class Subclass constructor function.
* @returns Base class constructor function.
*/
export function getBaseClass( Class : Function ) {
return Object.getPrototypeOf( Class.prototype ).constructor
}
export function assignToClassProto<T, K extends keyof T>( Class, definition : T, ...names : K[] ) : void {
for( let name of names ){
const value = definition[ name ];
value === void 0 || ( Class.prototype[ name ] = value );
}
}
/** Checks whenever given object is an empty hash `{}` */
export function isEmpty( obj : {} ) : boolean {
if( obj ){
for( let key in obj ){
if( obj.hasOwnProperty( key ) ){
return false;
}
}
}
return true;
}
export type Iteratee = ( value : any, key? : string | number ) => any;
function someArray( arr : any[], fun : Iteratee ) : any {
let result;
for( let i = 0; i < arr.length; i++ ){
if( result = fun( arr[ i ], i ) ){
return result;
}
}
}
function someObject( obj : {}, fun : Iteratee ) : any {
let result;
for( let key in obj ){
if( obj.hasOwnProperty( key ) ){
if( result = fun( obj[ key ], key ) ){
return result;
}
}
}
}
/** Similar to underscore `_.some` */
export function some( obj, fun : Iteratee ) : any {
if( Object.getPrototypeOf( obj ) === ArrayProto ){
return someArray( obj, fun );
}
else{
return someObject( obj, fun );
}
}
/** Similar to underscore `_.every` */
export function every( obj : { }, predicate : Iteratee ) : boolean {
return !some( obj, x => !predicate( x ) );
}
/** Similar to `getOwnPropertyDescriptor`, but traverse the whole prototype chain. */
export function getPropertyDescriptor( obj : {}, prop : string ) : PropertyDescriptor {
let desc : PropertyDescriptor;
for( let proto = obj; !desc && proto; proto = Object.getPrototypeOf( proto ) ) {
desc = Object.getOwnPropertyDescriptor( proto, prop );
}
return desc;
}
/** Similar to underscore `_.omit` */
export function omit( source : {}, ...rest : string[] ) : {}
export function omit( source ) : {} {
const dest = {}, discard = {};
for( let i = 1; i < arguments.length; i ++ ){
discard[ arguments[ i ] ] = true;
}
for( var name in source ) {
if( !discard.hasOwnProperty( name ) && source.hasOwnProperty( name ) ) {
dest[ name ] = source[ name ];
}
}
return dest;
}
/** map `source` object properties with a given function, and assign the result to the `dest` object.
* When `fun` returns `undefined`, skip this value.
*/
export function transform< A, B >( dest : { [ key : string ] : A }, source : { [ key : string ] : B }, fun : ( value : B, key : string ) => A | void ) : { [ key : string ] : A } {
for( var name in source ) {
if( source.hasOwnProperty( name ) ) {
var value = fun( source[ name ], name );
value === void 0 || ( dest[ name ] = < A >value );
}
}
return dest;
}
export function fastAssign< A >( dest : A, source : {} ) : A {
for( var name in source ) {
dest[ name ] = source[ name ];
}
return dest;
}
export function fastDefaults< A >( dest : A, source : {} ) : A {
for( var name in source ) {
if( dest[ name ] === void 0 ){
dest[ name ] = source[ name ];
}
}
return dest;
}
/** Similar to underscore `_.extend` and `Object.assign` */
export function assign< T >( dest : T, ...sources : Object[] ) : T
export function assign< T >( dest : T, source : Object ) : T {
for( var name in source ) {
if( source.hasOwnProperty( name ) ) {
dest[ name ] = source[ name ];
}
}
if( arguments.length > 2 ){
for( let i = 2; i < arguments.length; i++ ){
const other = arguments[ i ];
other && assign( dest, other );
}
}
return dest;
}
/** Similar to underscore `_.keys` */
export function keys( o : any ) : string[]{
return o ? Object.keys( o ) : [];
}
/** Similar to underscore `_.once` */
export function once( func : Function ) : Function {
var memo, first = true;
return function() {
if ( first ) {
first = false;
memo = func.apply(this, arguments);
func = null;
}
return memo;
};
}
const ArrayProto = Array.prototype,
DateProto = Date.prototype,
ObjectProto = Object.prototype;
/**
* Determine whenever two values are not equal, deeply traversing
* arrays and plain JS objects (hashes). Dates are compared by enclosed timestamps, all other
* values are compared with strict comparison.
*/
export function notEqual( a : any, b : any) : boolean {
if( a === b ) return false;
if( a && b && typeof a == 'object' && typeof b == 'object' ) {
const protoA = Object.getPrototypeOf( a );
if( protoA !== Object.getPrototypeOf( b ) ) return true;
switch( protoA ){
case DateProto : return +a !== +b;
case ArrayProto : return arraysNotEqual( a, b );
case ObjectProto :
case null:
return objectsNotEqual( a, b );
}
}
return true;
}
function objectsNotEqual( a, b ) {
const keysA = Object.keys( a );
if( keysA.length !== Object.keys( b ).length ) return true;
for( let i = 0; i < keysA.length; i++ ) {
const key = keysA[ i ];
if( !b.hasOwnProperty( key ) || notEqual( a[ key ], b[ key ] ) ) {
return true;
}
}
return false;
}
function arraysNotEqual( a, b ) {
if( a.length !== b.length ) return true;
for( let i = 0; i < a.length; i++ ) {
if( notEqual( a[ i ], b[ i ] ) ) return true;
}
return false;
}
/**
* Create an object without Object prototype members except hasOwnProperty.
* @param obj - optional parameter to populate the hash map from.
*/
const HashProto = Object.create( null );
HashProto.hasOwnProperty = ObjectProto.hasOwnProperty;
export function hashMap( obj? ){
const hash = Object.create( HashProto );
return obj ? assign( hash, obj ) : hash;
}
export function compare( a : any, b : any ) : -1 | 0 | 1 {
// Handle strictly equal values.
if( a == b ) return 0;
// Handle nulls.
if( a == null ) return -1;
if( b == null ) return 1;
// No nulls. Convert values to primitives.
const av = a.valueOf(),
bv = b.valueOf();
return av < bv ? -1 :
av > bv ? 1 :
0;
}