I've created a JavaScript function that continuously accepts functions(params => string) until it receives an object, at which point it resolves the final string as a concatenation of all the functions invoked over the same object passed in at the end.
Here is the function:
export function prefixer(func1?: any) {
function identity(x: any) { return x; }
function curriedPrefixer(fns: any[]) {
return function next(param: any) {
if (typeof param == 'function') {
return curriedPrefixer(
fns.concat(param)
);
}
if (typeof param == 'object')
return fns.reduce(
(prev, next) => prev + next(param), ''
);
return undefined;
}
}
return curriedPrefixer([func1 || identity])
}
My challenge lies in defining the appropriate types for its parameters and return type so that users of this function can pass a generic type (to inform the function about the type of the final params object) and effectively use the output repeatedly.
Below is a test case with the function:
test('should auto prefix', () => {
let prefix1: any = prefixer((params: any) => `https://wwww.${params.domain}.com`)
let prefix2: any = prefix1(() => '/path/to/item')
let prefix3: any = prefix2((params: any) => `/${params.itemId}`)
let params = {
domain: 'google',
itemId: '5444'
}
let resolvedString1 = prefix1(params);
let resolvedString2 = prefix2(params);
let resolvedString3 = prefix3(params);
let trueResult1 = `https://wwww.${params.domain}.com`
let trueResult2 = `https://wwww.${params.domain}.com/path/to/item`
let trueResult3 = `https://wwww.${params.domain}.com/path/to/item/${params.itemId}`
expect(resolvedString1).toEqual(trueResult1);
expect(resolvedString2).toEqual(trueResult2);
expect(resolvedString3).toEqual(trueResult3);
});
I have attempted different ideas without success and haven't found a satisfactory answer on recursive functions in TypeScript. Here's one attempt I made which didn't resolve the types definition:
export function prefixer<T>(func1?: any) {
function identity(x: any) { return x; }
function curriedPrefixer<M>(fns: any[]) {
return function next<S>(param: S | M | ((p: S | M) => any)) {
if (typeof param == 'function') {
return curriedPrefixer(
fns.concat(param)
);
}
if (typeof param == 'object')
return fns.reduce(
(prev, next) => prev + next(param), ''
);
return undefined;
}
}
return curriedPrefixer<T>([func1 || identity])
}
// Still need to pass (p: any)...
let prefix1 = prefixer<{ domain: string }>((p: any) => `https://wwww.${p.domain}.com`)
let prefix2 = prefix1<{ itemId: string }>((p: any) => `https://wwww.${p.itemId}.com`)