Skip to content

Commit

Permalink
fix(BindAll): copy over static properties from base constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
steelsojka committed Jun 15, 2017
1 parent 66af6f9 commit 489aaa4
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 9 deletions.
9 changes: 9 additions & 0 deletions src/bindAll.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,13 @@ describe('bindAll', () => {
expect(myClass.hasOwnProperty('prop')).to.be.false;
expect(myClass.hasOwnProperty('prop2')).to.be.false;
});

it('should copy over any static properties on the constructor', () => {
@BindAll()
class MyClass {
static $inject = [];
}

expect(MyClass.$inject).to.be.an('array');
});
});
12 changes: 4 additions & 8 deletions src/bindAll.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import isFunction = require('lodash/isFunction');

import { copyMetadata } from './utils';
import { copyMetadata, wrapConstructor } from './utils';
import { InstanceChainMap } from './factory';

/**
Expand Down Expand Up @@ -28,15 +28,11 @@ import { InstanceChainMap } from './factory';
*/
export function BindAll(methods: string[] = []): ClassDecorator {
return (target: Function): Function => {
function BindAllWrapper(...args: any[]): any {
return wrapConstructor(target, function(Ctor: Function, ...args: any[]) {
bindAllMethods(target, this, methods);

target.apply(this, args);
}

BindAllWrapper.prototype = target.prototype;

return BindAllWrapper;
Ctor.apply(this, args);
});
};
}

Expand Down
47 changes: 47 additions & 0 deletions src/utils/assignAll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import without = require('lodash/without');
import attempt = require('lodash/attempt');
import isObject = require('lodash/isObject');

/**
* Assigns all properties from an object to another object including non enumerable
* properties.
* @export
* @template T
* @template U
* @param {T} to
* @param {U} from
* @param {string[]} [excludes=[]]
* @returns {T}
*/
export function assignAll<T, U>(to: T, from: U, excludes: string[] = []): T {
const properties = without(Object.getOwnPropertyNames(from), ...excludes);

for (const prop of properties) {
attempt(assignProperty, to, from, prop);
}

return to;
}

/**
* Assigns a property from one object to another while retaining descriptor properties.
* @export
* @template T
* @template U
* @param {T} to
* @param {U} from
* @param {string} prop
*/
export function assignProperty<T, U>(to: T, from: U, prop: string): void {
const descriptor = Object.getOwnPropertyDescriptor(to, prop);

if (!descriptor || descriptor.configurable) {
const srcDescriptor = Object.getOwnPropertyDescriptor(from, prop);

if (isObject(srcDescriptor)) {
Object.defineProperty(to, prop, srcDescriptor);
} else {
(to as any)[prop] = (from as any)[prop];
}
}
}
4 changes: 3 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ export * from './resolveFunction';
export * from './CompositeKeyWeakMap';
export * from './returnAtIndex';
export * from './copyMetaData';
export * from './bind';
export * from './bind';
export * from './wrapConstructor';
export * from './assignAll';
27 changes: 27 additions & 0 deletions src/utils/wrapConstructor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { assignAll } from './assignAll';

const PROPERTY_EXCLUDES = [
'length',
'name',
'arguments',
'called',
'prototype'
];

/**
* Wraps a constructor in a wrapper function and copies all static properties
* and methods to the new constructor.
* @export
* @param {Function} Ctor
* @param {(Ctor: Function, ...args: any[]) => any} wrapper
* @returns {Function}
*/
export function wrapConstructor(Ctor: Function, wrapper: (Ctor: Function, ...args: any[]) => any): Function {
function ConstructorWrapper(...args: any[]) {
return wrapper.call(this, Ctor, ...args);
}

ConstructorWrapper.prototype = Ctor.prototype;

return assignAll(ConstructorWrapper, Ctor, PROPERTY_EXCLUDES);
}

0 comments on commit 489aaa4

Please sign in to comment.