Skip to content

Instantly share code, notes, and snippets.

@RomkeVdMeulen
Last active February 27, 2018 12:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RomkeVdMeulen/ee3397452be61f6b3e23a3ab506b7c0a to your computer and use it in GitHub Desktop.
Save RomkeVdMeulen/ee3397452be61f6b3e23a3ab506b7c0a to your computer and use it in GitHub Desktop.
export function autocast<T>(type: {new (): T}) {
return function(target: any, key: string) {
makeAutocast(target, key, (newVal: any) => {
if (newVal === undefined) {
return undefined;
}
if (newVal instanceof type) {
return newVal;
}
return Object.assign(new type(), newVal);
});
};
}
export function autocastArray<T>(type: {new (): T}) {
return function(target: any, key: string) {
makeAutocast(target, key, (newVals: any[]) => {
return newVals.map(newVal => {
if (newVal === undefined) {
return undefined;
}
if (newVal instanceof type) {
return newVal;
}
return Object.assign(new type(), newVal);
});
});
};
}
function makeAutocast<T>(prototype: any, key: string, mapper: (value: any) => T) {
const values = new Map<any, any>();
Object.defineProperty(prototype, key, {
set(initialValue: any) {
Object.defineProperty(this, key, {
get() {
return values.get(this);
},
set(value: any) {
values.set(this, mapper(value));
},
enumerable: true,
});
this[key] = initialValue;
},
enumerable: true,
configurable: true,
});
}
describe("@autocast", () => {
it("ensures values assigned to the decorated property are converted to the desired type if necessary", () => {
class Composed {
prop: string;
get extended() {
return this.prop + "-extended";
}
constructor(values?: any) {
Object.assign(this, values);
}
}
class Composer {
@autocast(Composed)
a: Composed;
constructor(values?: any) {
Object.assign(this, values);
}
}
const manual = new Composer({a: new Composed({prop: "a"})});
expect(manual.a).to.be.an.instanceOf(Composed).with.property("prop", "a");
expect(manual.a.extended).to.equal("a-extended");
expect(Object.keys(manual)).to.deep.equal(["a"]);
const empty = new Composer();
expect(empty.a).to.be.undefined;
expect(Object.keys(empty)).to.be.empty;
const automatic = new Composer({a: {prop: "b"}});
expect(automatic.a).to.be.an.instanceOf(Composed).with.property("prop", "b");
expect(automatic.a.extended).to.equal("b-extended");
expect(Object.keys(automatic)).to.deep.equal(["a"]);
const undefinedValue = new Composer({a: undefined});
expect(undefinedValue.a).to.be.undefined;
const assignedLater = new Composer();
assignedLater.a = <any> {prop: "c"};
expect(assignedLater.a).to.be.an.instanceOf(Composed).with.property("prop", "c");
expect(assignedLater.a.extended).to.equal("c-extended");
});
});
describe("@autocastArray", () => {
it("ensures values in any array assigned to the decorated property are converted to the right type if necessary", () => {
class Composed {
prop: string;
get extended() {
return this.prop + "-extended";
}
constructor(values?: any) {
Object.assign(this, values);
}
}
class Composer {
@autocastArray(Composed)
a: Composed[];
constructor(values?: any) {
Object.assign(this, values);
}
}
const manual = new Composer({a: [new Composed({prop: "a"}), new Composed({prop: "b"})]});
expect(manual.a).to.be.an("array").with.lengthOf(2);
expect(manual.a[0]).to.be.an.instanceOf(Composed).with.property("prop", "a");
expect(manual.a[0].extended).to.equal("a-extended");
expect(manual.a[1].extended).to.equal("b-extended");
expect(Object.keys(manual)).to.deep.equal(["a"]);
const empty = new Composer();
expect(empty.a).to.be.undefined;
expect(Object.keys(empty)).to.be.empty;
const automatic = new Composer({a: [{prop: "c"}, {prop: "d"}]});
expect(automatic.a).to.be.an("array").with.lengthOf(2);
expect(automatic.a[0]).to.be.an.instanceOf(Composed).with.property("prop", "c");
expect(automatic.a[0].extended).to.equal("c-extended");
expect(automatic.a[1].extended).to.equal("d-extended");
expect(manual.a[0]).to.be.an.instanceOf(Composed).with.property("prop", "a");
expect(Object.keys(automatic)).to.deep.equal(["a"]);
const undefinedValue = new Composer({a: [{prop: "c"}, undefined]});
expect(undefinedValue.a[0]).to.be.an.instanceOf(Composed).with.property("prop", "c");
expect(undefinedValue.a[1]).to.be.undefined;
const assignedLater = new Composer();
assignedLater.a = <any> [{prop: "e"}, {prop: "f"}];
expect(assignedLater.a).to.be.an("array").with.lengthOf(2);
expect(assignedLater.a[0]).to.be.an.instanceOf(Composed).with.property("prop", "e");
expect(assignedLater.a[0].extended).to.equal("e-extended");
expect(assignedLater.a[1].extended).to.equal("f-extended");
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment