I have a class called Conv. (Code at the end) It's for converting Objects via the run Function. In the "real" version there is also a runInverse() for simplicity reasons it's left out in this example.
i.e I can create a Conv and use it like this:
function fn(s: String, inv: Boolean) {
inv ? "inversed" : s
}
let conv = new Conv();
conv.fromPathToWithFn(data.id, fn, "internal_id");
conv.run({ data: { id: "1234" }}); // => { internal_id: "1234" }
The Converter is essentially a library and now I want to be able to define this fn as part of the conv but without modifying the Class on it's own.
That's so I'm able to access other values from the whole run / runInverse Object so that fn could look like this:
function fn(s: String, inv: Boolean) {
if (this.payload.otherValue) {
return "nice";
} else {
return "s";
}
}
And a request then would look like:
conv.run({ data: { id: "1234" }, otherValue: "defined" }) // => { internal_id: "nice" });
Here the class
export class Conv {
converted: Array<any> = []; // The blueprint of conversions to be done
inverse: Boolean = false; // Whether it's a normal or a inverse conversion (import/export)
newPayload: object = {}; // the converted vehicle
constructor() { }
/**
* Copies a value from one path into another
*
* @function fromPathTo
* @memberof Conv
*
* @param {String} from Source-Path
* @param {String} to Destination-Path
* @return {void}
*/
fromPathTo(from: String, to: String) {
this.converted.push({ from, to });
}
/**
* Takes the value in the Source-Path and uses it as an argument on the Converter-Function before putting it
* into the Destination-Path
*
* @function fromPathToWithFn
* @memberof Conv
*
* @param {String} from Source-Path
* @param {Function} fn Converter-Function
* @param {String} to Destination-Path
*/
fromPathToWithFn(from: String, fn: Function, to: String) {
this.converted.push({ from, fn, to });
}
/**
*
* @param {Object} payload The vehicle to be converted
* @returns {Object} newPayload The converted vehicle
*/
run(payload: Object, parentString: String = "") {
this.inverse = false;
/**
* Without this if-Statement we can't use default values
*/
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
return payload;
}
for (let prop in payload) {
let currentPath: string = prop;
if (parentString !== "") {
currentPath = parentString + "." + prop;
}
if (
typeof payload[prop] === "object" && !Array.isArray(payload[prop])
) {
this.run(payload[prop], currentPath);
} else {
/**
* @param {string} newProp Destination-Path
*/
let newProp: string = this.converted.find(
(_) => _.from === currentPath
)?.to;
/**
* @param {Function|undefined} func Converter-Function
*/
let func: Function | undefined = this.converted.find(
(_) => _.from === currentPath
)?.fn;
/**
* @param {string|null} def Default-Value
*/
let def: String | null = this.converted.find(
(_) => _.from === currentPath
)?.def;
// Check wether Value should even be used in this conversion
if (newProp === undefined || newProp === null) {
break;
}
if (newProp && newProp !== prop) {
// Only call the function if it's not undefined
// and set the Destination-Path to the default value if the Source-Value is null
typeof func !== "undefined"
? lodash.set(
this.newPayload,
newProp,
func(this.run(lodash.get(payload, prop)), this.inverse)
)
: this.run(lodash.get(payload, prop)) === null ||
typeof this.run(lodash.get(payload, prop)) === "undefined"
? lodash.set(this.newPayload, newProp, def)
: lodash.set(
this.newPayload,
newProp,
this.run(lodash.get(payload, prop))
);
} else {
// Only call the function if it's not undefined
// and set the Destination-Path to the default value if the Source-Value is null
typeof func !== "undefined"
? lodash.set(
this.newPayload,
prop,
func(this.run(lodash.get(payload, prop)), this.inverse)
)
: this.run(lodash.get(this.newPayload, currentPath)) === null ||
typeof this.run(lodash.get(this.newPayload, prop)) === "undefined"
? lodash.set(this.newPayload, currentPath, def)
: lodash.set(
this.newPayload,
currentPath,
this.run(lodash.get(payload, prop))
);
}
}
}
return this.newPayload;
}
}