From 2be4acf455ebec86e846854dbab57bd0bfbbceb7 Mon Sep 17 00:00:00 2001 From: Ray Epps Date: Tue, 20 Feb 2024 21:32:44 -0700 Subject: [PATCH] improve memo, partial, compose, and chain function types (#379) * improve memo, partial, compose, and chain function types --------- Co-authored-by: Ray Epps --- .github/workflows/test-on-master.yml | 2 +- .github/workflows/test-on-pr.yml | 6 +- cdn/radash.esm.js | 18 +- cdn/radash.js | 18 +- cdn/radash.min.js | 2 +- docs/curry/chain.mdx | 37 ++- package.json | 2 +- src/curry.ts | 471 +++++++++++++++++++++++---- src/tests/curry.test.ts | 147 +++++++-- 9 files changed, 577 insertions(+), 126 deletions(-) diff --git a/.github/workflows/test-on-master.yml b/.github/workflows/test-on-master.yml index 014ce64b..bffa44ed 100644 --- a/.github/workflows/test-on-master.yml +++ b/.github/workflows/test-on-master.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [14.x, 16.x, 18.x] + node-version: [14.x, 16.x, 18.17.1] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} diff --git a/.github/workflows/test-on-pr.yml b/.github/workflows/test-on-pr.yml index e5ed542d..17354720 100644 --- a/.github/workflows/test-on-pr.yml +++ b/.github/workflows/test-on-pr.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [14.x, 16.x, 18.x] + node-version: [14.x, 16.x, 18.17.1] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16.x' + node-version: '20.x' - run: yarn - run: yarn format:check verify-cdn-build-matches-src: @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16.x' + node-version: '20.x' - run: yarn - run: npm install -g checksum - name: Calc current cdn checksums diff --git a/cdn/radash.esm.js b/cdn/radash.esm.js index 4b6ce7cf..a0a63559 100644 --- a/cdn/radash.esm.js +++ b/cdn/radash.esm.js @@ -487,15 +487,15 @@ const guard = (func, shouldGuard) => { }; function chain(...funcs) { - return function forInitialArg(initialArg) { - return funcs.reduce((acc, fn) => fn(acc), initialArg); + return (...args) => { + return funcs.slice(1).reduce((acc, fn) => fn(acc), funcs[0](...args)); }; } -const compose = (...funcs) => { +function compose(...funcs) { return funcs.reverse().reduce((acc, fn) => fn(acc)); -}; +} const partial = (fn, ...args) => { - return (...rest) => fn(...args, ...rest); + return (...rest) => fn(...[...args, ...rest]); }; const partob = (fn, argobj) => { return (restobj) => fn({ @@ -711,15 +711,15 @@ const omit = (obj, keys2) => { const get = (value, path, defaultValue) => { const segments = path.split(/[\.\[\]]/g); let current = value; - for (let key of segments) { + for (const key of segments) { if (current === null) return defaultValue; if (current === void 0) return defaultValue; - key = key.replace(/['"]/g, ""); - if (key.trim() === "") + const dequoted = key.replace(/['"]/g, ""); + if (dequoted.trim() === "") continue; - current = current[key]; + current = current[dequoted]; } if (current === void 0) return defaultValue; diff --git a/cdn/radash.js b/cdn/radash.js index 58be43be..ca7ebea6 100644 --- a/cdn/radash.js +++ b/cdn/radash.js @@ -490,15 +490,15 @@ var radash = (function (exports) { }; function chain(...funcs) { - return function forInitialArg(initialArg) { - return funcs.reduce((acc, fn) => fn(acc), initialArg); + return (...args) => { + return funcs.slice(1).reduce((acc, fn) => fn(acc), funcs[0](...args)); }; } - const compose = (...funcs) => { + function compose(...funcs) { return funcs.reverse().reduce((acc, fn) => fn(acc)); - }; + } const partial = (fn, ...args) => { - return (...rest) => fn(...args, ...rest); + return (...rest) => fn(...[...args, ...rest]); }; const partob = (fn, argobj) => { return (restobj) => fn({ @@ -714,15 +714,15 @@ var radash = (function (exports) { const get = (value, path, defaultValue) => { const segments = path.split(/[\.\[\]]/g); let current = value; - for (let key of segments) { + for (const key of segments) { if (current === null) return defaultValue; if (current === void 0) return defaultValue; - key = key.replace(/['"]/g, ""); - if (key.trim() === "") + const dequoted = key.replace(/['"]/g, ""); + if (dequoted.trim() === "") continue; - current = current[key]; + current = current[dequoted]; } if (current === void 0) return defaultValue; diff --git a/cdn/radash.min.js b/cdn/radash.min.js index efc40c7a..f9363f50 100644 --- a/cdn/radash.min.js +++ b/cdn/radash.min.js @@ -1 +1 @@ -var radash=function(u){"use strict";const E=t=>!!t&&t.constructor===Symbol,w=Array.isArray,k=t=>!!t&&t.constructor===Object,N=t=>t==null||typeof t!="object"&&typeof t!="function",y=t=>!!(t&&t.constructor&&t.call&&t.apply),K=t=>typeof t=="string"||t instanceof String,W=t=>h(t)&&t%1===0,J=t=>h(t)&&t%1!==0,h=t=>{try{return Number(t)===t}catch{return!1}},T=t=>Object.prototype.toString.call(t)==="[object Date]",j=t=>!(!t||!t.then||!y(t.then)),X=t=>{if(t===!0||t===!1||t==null)return!0;if(h(t))return t===0;if(T(t))return isNaN(t.getTime());if(y(t)||E(t))return!1;const e=t.length;if(h(e))return e===0;const n=t.size;return h(n)?n===0:Object.keys(t).length===0},z=(t,e)=>{if(Object.is(t,e))return!0;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.toString()===e.toString();if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;const n=Reflect.ownKeys(t),r=Reflect.ownKeys(e);if(n.length!==r.length)return!1;for(let s=0;st.reduce((n,r)=>{const s=e(r);return n[s]||(n[s]=[]),n[s].push(r),n},{});function H(...t){return!t||!t.length?[]:new Array(Math.max(...t.map(({length:e})=>e))).fill([]).map((e,n)=>t.map(r=>r[n]))}function Q(t,e){if(!t||!t.length)return{};const n=y(e)?e:w(e)?(r,s)=>e[s]:(r,s)=>e;return t.reduce((r,s,i)=>(r[s]=n(s,i),r),{})}const O=(t,e)=>!t||(t.length??0)===0?null:t.reduce(e),V=(t,e)=>(t||[]).reduce((n,r)=>n+(e?e(r):r),0),G=(t,e=void 0)=>t?.length>0?t[0]:e,x=(t,e=void 0)=>t?.length>0?t[t.length-1]:e,S=(t,e,n=!1)=>{if(!t)return[];const r=(i,c)=>e(i)-e(c),s=(i,c)=>e(c)-e(i);return t.slice().sort(n===!0?s:r)},tt=(t,e,n="asc")=>{if(!t)return[];const r=(i,c)=>`${e(i)}`.localeCompare(e(c)),s=(i,c)=>`${e(c)}`.localeCompare(e(i));return t.slice().sort(n==="desc"?s:r)},et=(t,e)=>t?t.reduce((n,r)=>{const s=e(r);return n[s]=(n[s]??0)+1,n},{}):{},nt=(t,e,n)=>{if(!t)return[];if(e===void 0)return[...t];for(let r=0;rr)=>t.reduce((r,s)=>(r[e(s)]=n(s),r),{}),rt=(t,e,n)=>t?t.reduce((r,s,i)=>(n(s,i)&&r.push(e(s,i)),r),[]):[];function st(t,e){const n=e??(r=>r);return O(t,(r,s)=>n(r)>n(s)?r:s)}function ut(t,e){const n=e??(r=>r);return O(t,(r,s)=>n(r){const n=Math.ceil(t.length/e);return new Array(n).fill(null).map((r,s)=>t.slice(s*e,s*e+e))},ct=(t,e)=>{const n=t.reduce((r,s)=>{const i=e?e(s):s;return r[i]||(r[i]=s),r},{});return Object.values(n)};function*A(t,e,n=s=>s,r=1){const s=y(n)?n:()=>n,i=e?t:0,c=e??t;for(let o=i;o<=c&&(yield s(o),!(o+r>c));o+=r);}const C=(t,e,n,r)=>Array.from(A(t,e,n,r)),ot=t=>t.reduce((e,n)=>(e.push(...n),e),[]),lt=(t,e,n)=>{if(!t||!e)return!1;const r=n??(i=>i),s=e.reduce((i,c)=>(i[r(c)]=!0,i),{});return t.some(i=>s[r(i)])},L=(t,e)=>t?t.reduce((n,r)=>{const[s,i]=n;return e(r)?[[...s,r],i]:[s,[...i,r]]},[[],[]]):[[],[]],ft=(t,e,n)=>!e&&!t?[]:e?t?n?t.reduce((r,s)=>{const i=e.find(c=>n(s)===n(c));return i?r.push(i):r.push(s),r},[]):t:[]:t,at=(t,e,n)=>{if(!t&&!e)return[];if(!e)return[...t];if(!t)return[e];for(let r=0;r{if(!t&&!e)return[];if(!t)return[e];if(!e)return[...t];const s=n?(o,a)=>n(o,a)===n(e,a):o=>o===e;return t.find(s)?t.filter((o,a)=>!s(o,a)):(r?.strategy??"append")==="append"?[...t,e]:[e,...t]},gt=t=>t?.filter(e=>!!e)??[],M=(t,e,n)=>{let r=n;for(let s=1;s<=t;s++)r=e(r,s);return r},ht=(t,e,n=r=>r)=>{if(!t?.length&&!e?.length)return[];if(t?.length===void 0)return[...e];if(!e?.length)return[...t];const r=e.reduce((s,i)=>(s[n(i)]=!0,s),{});return t.filter(s=>!r[n(s)])};function mt(t,e){if(t.length===0)return t;const n=e%t.length;return n===0?t:[...t.slice(-n,t.length),...t.slice(0,-n)]}const wt=async(t,e,n)=>{const r=n!==void 0;if(!r&&t?.length<1)throw new Error("Cannot reduce empty array with no init value");const s=r?t:t.slice(1);let i=r?n:t[0];for(const[c,o]of s.entries())i=await e(i,o,c);return i},yt=async(t,e)=>{if(!t)return[];let n=[],r=0;for(const s of t){const i=await e(s,r++);n.push(i)}return n},pt=async t=>{const e=[],n=(i,c)=>e.push({fn:i,rethrow:c?.rethrow??!1}),[r,s]=await m(t)(n);for(const{fn:i,rethrow:c}of e){const[o]=await m(i)(r);if(o&&c)throw o}if(r)throw r;return s};class Z extends Error{constructor(e=[]){super();const n=e.find(r=>r.name)?.name??"";this.name=`AggregateError(${n}...)`,this.message=`AggregateError with ${e.length} errors`,this.stack=e.find(r=>r.stack)?.stack??this.stack,this.errors=e}}const bt=async(t,e,n)=>{const r=e.map((d,b)=>({index:b,item:d})),s=async d=>{const b=[];for(;;){const l=r.pop();if(!l)return d(b);const[f,g]=await m(n)(l.item);b.push({error:f,result:g,index:l.index})}},i=C(1,t).map(()=>new Promise(s)),c=await Promise.all(i),[o,a]=L(S(c.flat(),d=>d.index),d=>!!d.error);if(o.length>0)throw new Z(o.map(d=>d.error));return a.map(d=>d.result)};async function kt(t){const e=w(t)?t.map(s=>[null,s]):Object.entries(t),n=await Promise.all(e.map(([s,i])=>i.then(c=>({result:c,exc:null,key:s})).catch(c=>({result:null,exc:c,key:s})))),r=n.filter(s=>s.exc);if(r.length>0)throw new Z(r.map(s=>s.exc));return w(t)?n.map(s=>s.result):n.reduce((s,i)=>({...s,[i.key]:i.result}),{})}const Ot=async(t,e)=>{const n=t?.times??3,r=t?.delay,s=t?.backoff??null;for(const i of A(1,n)){const[c,o]=await m(e)(a=>{throw{_exited:a}});if(!c)return o;if(c._exited)throw c._exited;if(i===n)throw c;r&&await $(r),s&&await $(s(i))}},$=t=>new Promise(e=>setTimeout(e,t)),m=t=>(...e)=>{try{const n=t(...e);return j(n)?n.then(r=>[void 0,r]).catch(r=>[r,void 0]):[void 0,n]}catch(n){return[n,void 0]}},At=(t,e)=>{const n=s=>{if(e&&!e(s))throw s},r=s=>s instanceof Promise;try{const s=t();return r(s)?s.catch(n):s}catch(s){return n(s)}};function Ct(...t){return function(n){return t.reduce((r,s)=>s(r),n)}}const $t=(...t)=>t.reverse().reduce((e,n)=>n(e)),Pt=(t,...e)=>(...n)=>t(...e,...n),_t=(t,e)=>n=>t({...e,...n}),Et=t=>new Proxy({},{get:(e,n)=>t(n)}),Nt=(t,e,n,r)=>function(...i){const c=n?n(...i):JSON.stringify({args:i}),o=t[c];if(o!==void 0&&(!o.exp||o.exp>new Date().getTime()))return o.value;const a=e(...i);return t[c]={exp:r?new Date().getTime()+r:null,value:a},a},Tt=(t,e={})=>Nt({},t,e.key??null,e.ttl??null),jt=({delay:t},e)=>{let n,r=!0;const s=(...i)=>{r?(clearTimeout(n),n=setTimeout(()=>{r&&e(...i),n=void 0},t)):e(...i)};return s.isPending=()=>n!==void 0,s.cancel=()=>{r=!1},s.flush=(...i)=>e(...i),s},zt=({interval:t},e)=>{let n=!0,r;const s=(...i)=>{n&&(e(...i),n=!1,r=setTimeout(()=>{n=!0,r=void 0},t))};return s.isThrottled=()=>r!==void 0,s},St=(t,e)=>{const n=()=>{};return new Proxy(Object.assign(n,t),{get:(r,s)=>r[s],set:(r,s,i)=>(r[s]=i,!0),apply:(r,s,i)=>e(Object.assign({},r))(...i)})},Bt=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseFloat(t);return isNaN(r)?n:r},D=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseInt(t);return isNaN(r)?n:r},Lt=(t,e=n=>n===void 0)=>t?Object.keys(t).reduce((r,s)=>(e(t[s])||(r[s]=t[s]),r),{}):{},P=(t,e)=>Object.keys(t).reduce((r,s)=>(r[e(s,t[s])]=t[s],r),{}),Mt=(t,e)=>Object.keys(t).reduce((r,s)=>(r[s]=e(t[s],s),r),{}),Zt=(t,e)=>t?Object.entries(t).reduce((n,[r,s])=>{const[i,c]=e(r,s);return n[i]=c,n},{}):{},Dt=t=>t?Object.keys(t).reduce((n,r)=>(n[t[r]]=r,n),{}):{},Ft=t=>P(t,e=>e.toLowerCase()),It=t=>P(t,e=>e.toUpperCase()),F=t=>{if(N(t))return t;if(typeof t=="function")return t.bind({});const e=new t.constructor;return Object.getOwnPropertyNames(t).forEach(n=>{e[n]=t[n]}),e},Rt=(t,e)=>{if(!t)return[];const n=Object.entries(t);return n.length===0?[]:n.reduce((r,s)=>(r.push(e(s[0],s[1])),r),[])},qt=(t,e)=>t?e.reduce((n,r)=>(Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]),n),{}):{},vt=(t,e)=>t?!e||e.length===0?t:e.reduce((n,r)=>(delete n[r],n),{...t}):{},I=(t,e,n)=>{const r=e.split(/[\.\[\]]/g);let s=t;for(const i of r){if(s===null||s===void 0)return n;i.trim()!==""&&(s=s[i])}return s===void 0?n:s},R=(t,e,n)=>{if(!t)return{};if(!e||n===void 0)return t;const r=e.split(/[\.\[\]]/g).filter(c=>!!c.trim()),s=c=>{if(r.length>1){const o=r.shift(),a=D(r[0],null)!==null;c[o]=c[o]===void 0?a?[]:{}:c[o],s(c[o])}else c[r[0]]=n},i=F(t);return s(i),i},q=(t,e)=>!t||!e?t??e??{}:Object.entries({...t,...e}).reduce((n,[r,s])=>({...n,[r]:(()=>k(t[r])?q(t[r],s):s)()}),{}),v=t=>{if(!t)return[];const e=(n,r)=>k(n)?Object.entries(n).flatMap(([s,i])=>e(i,[...r,s])):w(n)?n.flatMap((s,i)=>e(s,[...r,`${i}`])):[r.join(".")];return e(t,[])},Ut=t=>t?B(v(t),e=>e,e=>I(t,e)):{},Kt=t=>t?Object.keys(t).reduce((e,n)=>R(e,n,t[n]),{}):{},_=(t,e)=>Math.floor(Math.random()*(e-t+1)+t),Wt=t=>{const e=t.length;if(e===0)return null;const n=_(0,e-1);return t[n]},Jt=t=>t.map(e=>({rand:Math.random(),value:e})).sort((e,n)=>e.rand-n.rand).map(e=>e.value),Xt=(t,e="")=>{const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+e;return M(t,r=>r+n.charAt(_(0,n.length-1)),"")},Yt=(t,e=n=>`${n}`)=>{const{indexesByKey:n,itemsByIndex:r}=t.reduce((l,f,g)=>({indexesByKey:{...l.indexesByKey,[e(f)]:g},itemsByIndex:{...l.itemsByIndex,[g]:f}}),{indexesByKey:{},itemsByIndex:{}}),s=(l,f)=>n[e(l)]n[e(l)]>n[e(f)]?l:f,c=()=>r[0],o=()=>r[t.length-1],a=(l,f)=>r[n[e(l)]+1]??f??c(),d=(l,f)=>r[n[e(l)]-1]??f??o();return{min:s,max:i,first:c,last:o,next:a,previous:d,spin:(l,f)=>{if(f===0)return l;const g=Math.abs(f),ne=g>t.length?g%t.length:g;return C(0,ne-1).reduce(U=>f>0?a(U):d(U),l)}}},p=t=>{if(!t||t.length===0)return"";const e=t.toLowerCase();return e.substring(0,1).toUpperCase()+e.substring(1,e.length)},Ht=t=>{const e=t?.replace(/([A-Z])+/g,p)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}${r.charAt(0).toUpperCase()}${r.slice(1)}`)},Qt=(t,e)=>{const n=t?.replace(/([A-Z])+/g,p).split(/(?=[A-Z])|[\.\-\s_]/).map(s=>s.toLowerCase())??[];if(n.length===0)return"";if(n.length===1)return n[0];const r=n.reduce((s,i)=>`${s}_${i.toLowerCase()}`);return e?.splitOnNumber===!1?r:r.replace(/([A-Za-z]{1}[0-9]{1})/,s=>`${s[0]}_${s[1]}`)},Vt=t=>{const e=t?.replace(/([A-Z])+/g,p)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}-${r.toLowerCase()}`)},Gt=t=>{const e=t?.split(/[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("")},xt=t=>t?t.split(/(?=[A-Z])|[\.\-\s_]/).map(e=>e.trim()).filter(e=>!!e).map(e=>p(e.toLowerCase())).join(" "):"",te=(t,e,n=/\{\{(.+?)\}\}/g)=>Array.from(t.matchAll(n)).reduce((r,s)=>r.replace(s[0],e[s[1]]),t),ee=(t,e=" ")=>{if(!t)return"";const n=e.replace(/[\W]{1}/g,"\\$&"),r=new RegExp(`^[${n}]+|[${n}]+$`,"g");return t.replace(r,"")};return u.all=kt,u.alphabetical=tt,u.assign=q,u.boil=O,u.callable=St,u.camel=Ht,u.capitalize=p,u.chain=Ct,u.clone=F,u.cluster=it,u.compose=$t,u.construct=Kt,u.counting=et,u.crush=Ut,u.dash=Vt,u.debounce=jt,u.defer=pt,u.diff=ht,u.draw=Wt,u.first=G,u.flat=ot,u.fork=L,u.get=I,u.group=Y,u.guard=At,u.intersects=lt,u.invert=Dt,u.isArray=w,u.isDate=T,u.isEmpty=X,u.isEqual=z,u.isFloat=J,u.isFunction=y,u.isInt=W,u.isNumber=h,u.isObject=k,u.isPrimitive=N,u.isPromise=j,u.isString=K,u.isSymbol=E,u.iterate=M,u.keys=v,u.last=x,u.list=C,u.listify=Rt,u.lowerize=Ft,u.map=yt,u.mapEntries=Zt,u.mapKeys=P,u.mapValues=Mt,u.max=st,u.memo=Tt,u.merge=ft,u.min=ut,u.objectify=B,u.omit=vt,u.parallel=bt,u.partial=Pt,u.partob=_t,u.pascal=Gt,u.pick=qt,u.proxied=Et,u.random=_,u.range=A,u.reduce=wt,u.replace=nt,u.replaceOrAppend=at,u.retry=Ot,u.select=rt,u.series=Yt,u.set=R,u.shake=Lt,u.shift=mt,u.shuffle=Jt,u.sift=gt,u.sleep=$,u.snake=Qt,u.sort=S,u.sum=V,u.template=te,u.throttle=zt,u.title=xt,u.toFloat=Bt,u.toInt=D,u.toggle=dt,u.trim=ee,u.try=m,u.tryit=m,u.uid=Xt,u.unique=ct,u.upperize=It,u.zip=H,u.zipToObject=Q,u}({}); \ No newline at end of file +var radash=function(i){"use strict";const E=e=>!!e&&e.constructor===Symbol,w=Array.isArray,k=e=>!!e&&e.constructor===Object,N=e=>e==null||typeof e!="object"&&typeof e!="function",y=e=>!!(e&&e.constructor&&e.call&&e.apply),K=e=>typeof e=="string"||e instanceof String,W=e=>h(e)&&e%1===0,J=e=>h(e)&&e%1!==0,h=e=>{try{return Number(e)===e}catch{return!1}},T=e=>Object.prototype.toString.call(e)==="[object Date]",S=e=>!(!e||!e.then||!y(e.then)),X=e=>{if(e===!0||e===!1||e==null)return!0;if(h(e))return e===0;if(T(e))return isNaN(e.getTime());if(y(e)||E(e))return!1;const t=e.length;if(h(t))return t===0;const n=e.size;return h(n)?n===0:Object.keys(e).length===0},j=(e,t)=>{if(Object.is(e,t))return!0;if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(e instanceof RegExp&&t instanceof RegExp)return e.toString()===t.toString();if(typeof e!="object"||e===null||typeof t!="object"||t===null)return!1;const n=Reflect.ownKeys(e),r=Reflect.ownKeys(t);if(n.length!==r.length)return!1;for(let s=0;se.reduce((n,r)=>{const s=t(r);return n[s]||(n[s]=[]),n[s].push(r),n},{});function H(...e){return!e||!e.length?[]:new Array(Math.max(...e.map(({length:t})=>t))).fill([]).map((t,n)=>e.map(r=>r[n]))}function Q(e,t){if(!e||!e.length)return{};const n=y(t)?t:w(t)?(r,s)=>t[s]:(r,s)=>t;return e.reduce((r,s,u)=>(r[s]=n(s,u),r),{})}const O=(e,t)=>!e||(e.length??0)===0?null:e.reduce(t);function V(e,t){return(e||[]).reduce((n,r)=>n+(t?t(r):r),0)}const G=(e,t=void 0)=>e?.length>0?e[0]:t,x=(e,t=void 0)=>e?.length>0?e[e.length-1]:t,z=(e,t,n=!1)=>{if(!e)return[];const r=(u,c)=>t(u)-t(c),s=(u,c)=>t(c)-t(u);return e.slice().sort(n===!0?s:r)},ee=(e,t,n="asc")=>{if(!e)return[];const r=(u,c)=>`${t(u)}`.localeCompare(t(c)),s=(u,c)=>`${t(c)}`.localeCompare(t(u));return e.slice().sort(n==="desc"?s:r)},te=(e,t)=>e?e.reduce((n,r)=>{const s=t(r);return n[s]=(n[s]??0)+1,n},{}):{},ne=(e,t,n)=>{if(!e)return[];if(t===void 0)return[...e];for(let r=0;rr)=>e.reduce((r,s)=>(r[t(s)]=n(s),r),{}),re=(e,t,n)=>e?e.reduce((r,s,u)=>(n(s,u)&&r.push(t(s,u)),r),[]):[];function se(e,t){const n=t??(r=>r);return O(e,(r,s)=>n(r)>n(s)?r:s)}function ie(e,t){const n=t??(r=>r);return O(e,(r,s)=>n(r){const n=Math.ceil(e.length/t);return new Array(n).fill(null).map((r,s)=>e.slice(s*t,s*t+t))},ce=(e,t)=>{const n=e.reduce((r,s)=>{const u=t?t(s):s;return r[u]||(r[u]=s),r},{});return Object.values(n)};function*A(e,t,n=s=>s,r=1){const s=y(n)?n:()=>n,u=t?e:0,c=t??e;for(let o=u;o<=c&&(yield s(o),!(o+r>c));o+=r);}const C=(e,t,n,r)=>Array.from(A(e,t,n,r)),oe=e=>e.reduce((t,n)=>(t.push(...n),t),[]),fe=(e,t,n)=>{if(!e||!t)return!1;const r=n??(u=>u),s=t.reduce((u,c)=>(u[r(c)]=!0,u),{});return e.some(u=>s[r(u)])},R=(e,t)=>e?e.reduce((n,r)=>{const[s,u]=n;return t(r)?[[...s,r],u]:[s,[...u,r]]},[[],[]]):[[],[]],le=(e,t,n)=>!t&&!e?[]:t?e?n?e.reduce((r,s)=>{const u=t.find(c=>n(s)===n(c));return u?r.push(u):r.push(s),r},[]):e:[]:e,ae=(e,t,n)=>{if(!e&&!t)return[];if(!t)return[...e];if(!e)return[t];for(let r=0;r{if(!e&&!t)return[];if(!e)return[t];if(!t)return[...e];const s=n?(o,a)=>n(o,a)===n(t,a):o=>o===t;return e.find(s)?e.filter((o,a)=>!s(o,a)):(r?.strategy??"append")==="append"?[...e,t]:[t,...e]},ge=e=>e?.filter(t=>!!t)??[],B=(e,t,n)=>{let r=n;for(let s=1;s<=e;s++)r=t(r,s);return r},he=(e,t,n=r=>r)=>{if(!e?.length&&!t?.length)return[];if(e?.length===void 0)return[...t];if(!t?.length)return[...e];const r=t.reduce((s,u)=>(s[n(u)]=!0,s),{});return e.filter(s=>!r[n(s)])};function me(e,t){if(e.length===0)return e;const n=t%e.length;return n===0?e:[...e.slice(-n,e.length),...e.slice(0,-n)]}const we=async(e,t,n)=>{const r=n!==void 0;if(!r&&e?.length<1)throw new Error("Cannot reduce empty array with no init value");const s=r?e:e.slice(1);let u=r?n:e[0];for(const[c,o]of s.entries())u=await t(u,o,c);return u},ye=async(e,t)=>{if(!e)return[];let n=[],r=0;for(const s of e){const u=await t(s,r++);n.push(u)}return n},pe=async e=>{const t=[],n=(u,c)=>t.push({fn:u,rethrow:c?.rethrow??!1}),[r,s]=await m(e)(n);for(const{fn:u,rethrow:c}of t){const[o]=await m(u)(r);if(o&&c)throw o}if(r)throw r;return s};class L extends Error{constructor(t=[]){super();const n=t.find(r=>r.name)?.name??"";this.name=`AggregateError(${n}...)`,this.message=`AggregateError with ${t.length} errors`,this.stack=t.find(r=>r.stack)?.stack??this.stack,this.errors=t}}const be=async(e,t,n)=>{const r=t.map((d,b)=>({index:b,item:d})),s=async d=>{const b=[];for(;;){const f=r.pop();if(!f)return d(b);const[l,g]=await m(n)(f.item);b.push({error:l,result:g,index:f.index})}},u=C(1,e).map(()=>new Promise(s)),c=await Promise.all(u),[o,a]=R(z(c.flat(),d=>d.index),d=>!!d.error);if(o.length>0)throw new L(o.map(d=>d.error));return a.map(d=>d.result)};async function ke(e){const t=w(e)?e.map(s=>[null,s]):Object.entries(e),n=await Promise.all(t.map(([s,u])=>u.then(c=>({result:c,exc:null,key:s})).catch(c=>({result:null,exc:c,key:s})))),r=n.filter(s=>s.exc);if(r.length>0)throw new L(r.map(s=>s.exc));return w(e)?n.map(s=>s.result):n.reduce((s,u)=>({...s,[u.key]:u.result}),{})}const Oe=async(e,t)=>{const n=e?.times??3,r=e?.delay,s=e?.backoff??null;for(const u of A(1,n)){const[c,o]=await m(t)(a=>{throw{_exited:a}});if(!c)return o;if(c._exited)throw c._exited;if(u===n)throw c;r&&await $(r),s&&await $(s(u))}},$=e=>new Promise(t=>setTimeout(t,e)),m=e=>(...t)=>{try{const n=e(...t);return S(n)?n.then(r=>[void 0,r]).catch(r=>[r,void 0]):[void 0,n]}catch(n){return[n,void 0]}},Ae=(e,t)=>{const n=s=>{if(t&&!t(s))throw s},r=s=>s instanceof Promise;try{const s=e();return r(s)?s.catch(n):s}catch(s){return n(s)}};function Ce(...e){return(...t)=>e.slice(1).reduce((n,r)=>r(n),e[0](...t))}function $e(...e){return e.reverse().reduce((t,n)=>n(t))}const Pe=(e,...t)=>(...n)=>e(...t,...n),_e=(e,t)=>n=>e({...t,...n}),Ee=e=>new Proxy({},{get:(t,n)=>e(n)}),Ne=(e,t,n,r)=>function(...u){const c=n?n(...u):JSON.stringify({args:u}),o=e[c];if(o!==void 0&&(!o.exp||o.exp>new Date().getTime()))return o.value;const a=t(...u);return e[c]={exp:r?new Date().getTime()+r:null,value:a},a},Te=(e,t={})=>Ne({},e,t.key??null,t.ttl??null),Se=({delay:e},t)=>{let n,r=!0;const s=(...u)=>{r?(clearTimeout(n),n=setTimeout(()=>{r&&t(...u),n=void 0},e)):t(...u)};return s.isPending=()=>n!==void 0,s.cancel=()=>{r=!1},s.flush=(...u)=>t(...u),s},je=({interval:e},t)=>{let n=!0,r;const s=(...u)=>{n&&(t(...u),n=!1,r=setTimeout(()=>{n=!0,r=void 0},e))};return s.isThrottled=()=>r!==void 0,s},ze=(e,t)=>{const n=()=>{};return new Proxy(Object.assign(n,e),{get:(r,s)=>r[s],set:(r,s,u)=>(r[s]=u,!0),apply:(r,s,u)=>t(Object.assign({},r))(...u)})};function Me(e,t,n){return typeof e=="number"&&typeof t=="number"&&(typeof n>"u"||typeof n=="number")?(typeof n>"u"&&(n=t,t=0),e>=Math.min(t,n)&&e{const n=t===void 0?0:t;if(e==null)return n;const r=parseFloat(e);return isNaN(r)?n:r},Z=(e,t)=>{const n=t===void 0?0:t;if(e==null)return n;const r=parseInt(e);return isNaN(r)?n:r},Be=(e,t=n=>n===void 0)=>e?Object.keys(e).reduce((r,s)=>(t(e[s])||(r[s]=e[s]),r),{}):{},P=(e,t)=>Object.keys(e).reduce((r,s)=>(r[t(s,e[s])]=e[s],r),{}),Le=(e,t)=>Object.keys(e).reduce((r,s)=>(r[s]=t(e[s],s),r),{}),Ze=(e,t)=>e?Object.entries(e).reduce((n,[r,s])=>{const[u,c]=t(r,s);return n[u]=c,n},{}):{},De=e=>e?Object.keys(e).reduce((n,r)=>(n[e[r]]=r,n),{}):{},Fe=e=>P(e,t=>t.toLowerCase()),qe=e=>P(e,t=>t.toUpperCase()),D=e=>{if(N(e))return e;if(typeof e=="function")return e.bind({});const t=new e.constructor;return Object.getOwnPropertyNames(e).forEach(n=>{t[n]=e[n]}),t},Ie=(e,t)=>{if(!e)return[];const n=Object.entries(e);return n.length===0?[]:n.reduce((r,s)=>(r.push(t(s[0],s[1])),r),[])},ve=(e,t)=>e?t.reduce((n,r)=>(Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]),n),{}):{},Ue=(e,t)=>e?!t||t.length===0?e:t.reduce((n,r)=>(delete n[r],n),{...e}):{},F=(e,t,n)=>{const r=t.split(/[\.\[\]]/g);let s=e;for(const u of r){if(s===null||s===void 0)return n;const c=u.replace(/['"]/g,"");c.trim()!==""&&(s=s[c])}return s===void 0?n:s},q=(e,t,n)=>{if(!e)return{};if(!t||n===void 0)return e;const r=t.split(/[\.\[\]]/g).filter(c=>!!c.trim()),s=c=>{if(r.length>1){const o=r.shift(),a=Z(r[0],null)!==null;c[o]=c[o]===void 0?a?[]:{}:c[o],s(c[o])}else c[r[0]]=n},u=D(e);return s(u),u},I=(e,t)=>!e||!t?e??t??{}:Object.entries({...e,...t}).reduce((n,[r,s])=>({...n,[r]:(()=>k(e[r])?I(e[r],s):s)()}),{}),v=e=>{if(!e)return[];const t=(n,r)=>k(n)?Object.entries(n).flatMap(([s,u])=>t(u,[...r,s])):w(n)?n.flatMap((s,u)=>t(s,[...r,`${u}`])):[r.join(".")];return t(e,[])},Ke=e=>e?M(v(e),t=>t,t=>F(e,t)):{},We=e=>e?Object.keys(e).reduce((t,n)=>q(t,n,e[n]),{}):{},_=(e,t)=>Math.floor(Math.random()*(t-e+1)+e),Je=e=>{const t=e.length;if(t===0)return null;const n=_(0,t-1);return e[n]},Xe=e=>e.map(t=>({rand:Math.random(),value:t})).sort((t,n)=>t.rand-n.rand).map(t=>t.value),Ye=(e,t="")=>{const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+t;return B(e,r=>r+n.charAt(_(0,n.length-1)),"")},He=(e,t=n=>`${n}`)=>{const{indexesByKey:n,itemsByIndex:r}=e.reduce((f,l,g)=>({indexesByKey:{...f.indexesByKey,[t(l)]:g},itemsByIndex:{...f.itemsByIndex,[g]:l}}),{indexesByKey:{},itemsByIndex:{}}),s=(f,l)=>n[t(f)]n[t(f)]>n[t(l)]?f:l,c=()=>r[0],o=()=>r[e.length-1],a=(f,l)=>r[n[t(f)]+1]??l??c(),d=(f,l)=>r[n[t(f)]-1]??l??o();return{min:s,max:u,first:c,last:o,next:a,previous:d,spin:(f,l)=>{if(l===0)return f;const g=Math.abs(l),rt=g>e.length?g%e.length:g;return C(0,rt-1).reduce(U=>l>0?a(U):d(U),f)}}},p=e=>{if(!e||e.length===0)return"";const t=e.toLowerCase();return t.substring(0,1).toUpperCase()+t.substring(1,t.length)},Qe=e=>{const t=e?.replace(/([A-Z])+/g,p)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return t.length===0?"":t.length===1?t[0]:t.reduce((n,r)=>`${n}${r.charAt(0).toUpperCase()}${r.slice(1)}`)},Ve=(e,t)=>{const n=e?.replace(/([A-Z])+/g,p).split(/(?=[A-Z])|[\.\-\s_]/).map(s=>s.toLowerCase())??[];if(n.length===0)return"";if(n.length===1)return n[0];const r=n.reduce((s,u)=>`${s}_${u.toLowerCase()}`);return t?.splitOnNumber===!1?r:r.replace(/([A-Za-z]{1}[0-9]{1})/,s=>`${s[0]}_${s[1]}`)},Ge=e=>{const t=e?.replace(/([A-Z])+/g,p)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return t.length===0?"":t.length===1?t[0]:t.reduce((n,r)=>`${n}-${r.toLowerCase()}`)},xe=e=>{const t=e?.split(/[\.\-\s_]/).map(n=>n.toLowerCase())??[];return t.length===0?"":t.map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("")},et=e=>e?e.split(/(?=[A-Z])|[\.\-\s_]/).map(t=>t.trim()).filter(t=>!!t).map(t=>p(t.toLowerCase())).join(" "):"",tt=(e,t,n=/\{\{(.+?)\}\}/g)=>Array.from(e.matchAll(n)).reduce((r,s)=>r.replace(s[0],t[s[1]]),e),nt=(e,t=" ")=>{if(!e)return"";const n=t.replace(/[\W]{1}/g,"\\$&"),r=new RegExp(`^[${n}]+|[${n}]+$`,"g");return e.replace(r,"")};return i.all=ke,i.alphabetical=ee,i.assign=I,i.boil=O,i.callable=ze,i.camel=Qe,i.capitalize=p,i.chain=Ce,i.clone=D,i.cluster=ue,i.compose=$e,i.construct=We,i.counting=te,i.crush=Ke,i.dash=Ge,i.debounce=Se,i.defer=pe,i.diff=he,i.draw=Je,i.first=G,i.flat=oe,i.fork=R,i.get=F,i.group=Y,i.guard=Ae,i.inRange=Me,i.intersects=fe,i.invert=De,i.isArray=w,i.isDate=T,i.isEmpty=X,i.isEqual=j,i.isFloat=J,i.isFunction=y,i.isInt=W,i.isNumber=h,i.isObject=k,i.isPrimitive=N,i.isPromise=S,i.isString=K,i.isSymbol=E,i.iterate=B,i.keys=v,i.last=x,i.list=C,i.listify=Ie,i.lowerize=Fe,i.map=ye,i.mapEntries=Ze,i.mapKeys=P,i.mapValues=Le,i.max=se,i.memo=Te,i.merge=le,i.min=ie,i.objectify=M,i.omit=Ue,i.parallel=be,i.partial=Pe,i.partob=_e,i.pascal=xe,i.pick=ve,i.proxied=Ee,i.random=_,i.range=A,i.reduce=we,i.replace=ne,i.replaceOrAppend=ae,i.retry=Oe,i.select=re,i.series=He,i.set=q,i.shake=Be,i.shift=me,i.shuffle=Xe,i.sift=ge,i.sleep=$,i.snake=Ve,i.sort=z,i.sum=V,i.template=tt,i.throttle=je,i.title=et,i.toFloat=Re,i.toInt=Z,i.toggle=de,i.trim=nt,i.try=m,i.tryit=m,i.uid=Ye,i.unique=ce,i.upperize=qe,i.zip=H,i.zipToObject=Q,i}({}); diff --git a/docs/curry/chain.mdx b/docs/curry/chain.mdx index 51565dc7..3e27f540 100644 --- a/docs/curry/chain.mdx +++ b/docs/curry/chain.mdx @@ -21,28 +21,29 @@ chained(0) // => 10 chained(7) // => 24 ``` -### More example +### Example ```ts import { chain } from 'radash' -type User = { id: number; name: string } -const users: User[] = [ - { id: 1, name: 'John Doe' }, - { id: 2, name: 'John Smith' }, - { id: 3, name: 'John Wick' } -] -function getName(item: T) { - return item.name; -} -function upperCase(text: string): Uppercase { - return text.toUpperCase() as Uppercase; +type Deity = { + name: string + rank: number } -const getUpperName = chain>(getName, upperCase) -// ^ Use chain function here +const gods: Deity[] = [ + { rank: 8, name: 'Ra' }, + { rank: 7, name: 'Zeus' }, + { rank: 9, name: 'Loki' } +] + +const getName = (god: Deity) => item.name +const upperCase = (text: string) => text.toUpperCase() as Uppercase + +const getUpperName = chain( + getName, + upperCase +) -getUpperName(users[0]) // => 'JOHN DOE' -users.map((user) => getUpperName(user)) // => ['JOHN DOE', 'JOHN SMITH', 'JOHN WICK'] -users.map(getUpperName) // => ['JOHN DOE', 'JOHN SMITH', 'JOHN WICK'] -// ^ use chained function as a Point-free +getUpperName(gods[0]) // => 'RA' +gods.map(getUpperName) // => ['RA', 'ZEUS', 'LOKI'] ``` diff --git a/package.json b/package.json index 0c109b57..a6aa123b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "radash", - "version": "11.0.0", + "version": "12.0.0", "description": "Functional utility library - modern, simple, typed, powerful", "main": "dist/cjs/index.cjs", "module": "dist/esm/index.mjs", diff --git a/src/curry.ts b/src/curry.ts index 4bd57846..1d359441 100644 --- a/src/curry.ts +++ b/src/curry.ts @@ -1,64 +1,407 @@ -export type UnaryFunc = (arg: T) => R -export type Func = ( - ...args: TArgs[] -) => KReturn - -export function chain( - fn1: UnaryFunc, - fn2: UnaryFunc -): UnaryFunc -export function chain( - fn1: UnaryFunc, - fn2: UnaryFunc, - fn3: UnaryFunc -): UnaryFunc -export function chain( - fn1: UnaryFunc, - fn2: UnaryFunc, - fn3: UnaryFunc, - fn4: UnaryFunc -): UnaryFunc -export function chain( - fn1: UnaryFunc, - fn2: UnaryFunc, - fn3: UnaryFunc, - fn4: UnaryFunc, - fn5: UnaryFunc -): UnaryFunc -export function chain( - fn1: UnaryFunc, - fn2: UnaryFunc, - fn3: UnaryFunc, - fn4: UnaryFunc, - fn5: UnaryFunc, - fn6: UnaryFunc -): UnaryFunc -export function chain( - fn1: UnaryFunc, - fn2: UnaryFunc, - fn3: UnaryFunc, - fn4: UnaryFunc, - fn5: UnaryFunc, - fn6: UnaryFunc, - fn7: UnaryFunc -): UnaryFunc -export function chain( - ...fns: ((arg: any) => any)[] -): UnaryFunc -export function chain(...funcs: Func[]): Func { - return function forInitialArg(initialArg: Parameters[0]) { - return funcs.reduce((acc, fn) => fn(acc), initialArg) +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3 +): (...arg: T1) => T3 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4 +): (...arg: T1) => T4 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5 +): (...arg: T1) => T5 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5, + f5: (arg: T3) => T6 +): (...arg: T1) => T6 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5, + f5: (arg: T3) => T6, + f6: (arg: T3) => T7 +): (...arg: T1) => T7 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5, + f5: (arg: T3) => T6, + f6: (arg: T3) => T7, + f7: (arg: T3) => T8 +): (...arg: T1) => T8 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5, + f5: (arg: T3) => T6, + f6: (arg: T3) => T7, + f7: (arg: T3) => T8, + f8: (arg: T3) => T9 +): (...arg: T1) => T9 +export function chain( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5, + f5: (arg: T3) => T6, + f6: (arg: T3) => T7, + f7: (arg: T3) => T8, + f8: (arg: T3) => T9, + f9: (arg: T3) => T10 +): (...arg: T1) => T10 +export function chain< + T1 extends any[], + T2, + T3, + T4, + T5, + T6, + T7, + T8, + T9, + T10, + T11 +>( + f1: (...arg: T1) => T2, + f2: (arg: T2) => T3, + f3: (arg: T3) => T4, + f4: (arg: T3) => T5, + f5: (arg: T3) => T6, + f6: (arg: T3) => T7, + f7: (arg: T3) => T8, + f8: (arg: T3) => T9, + f9: (arg: T3) => T10, + f10: (arg: T3) => T11 +): (...arg: T1) => T11 +export function chain(...funcs: ((...args: any[]) => any)[]) { + return (...args: any[]) => { + return funcs.slice(1).reduce((acc, fn) => fn(acc), funcs[0](...args)) } } -export const compose = (...funcs: Func[]) => { +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => LastResult + ) => (...args: F1Args) => F1Result, + last: (...args: F1NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2Result, + F2NextArgs extends any[], + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => LastResult + ) => (...args: F1NextArgs) => F2Result, + last: (...args: F2NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => LastResult + ) => (...args: F2NextArgs) => F3Result, + last: (...args: F3NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + F4NextArgs extends any[], + F4Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => F4Result + ) => (...args: F2NextArgs) => F3Result, + f4: ( + next: (...args: F4NextArgs) => LastResult + ) => (...args: F3NextArgs) => F4Result, + last: (...args: F4NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + F4NextArgs extends any[], + F4Result, + F5NextArgs extends any[], + F5Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => F4Result + ) => (...args: F2NextArgs) => F3Result, + f4: ( + next: (...args: F4NextArgs) => F5Result + ) => (...args: F3NextArgs) => F4Result, + f5: ( + next: (...args: F5NextArgs) => LastResult + ) => (...args: F4NextArgs) => F5Result, + last: (...args: F5NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + F4NextArgs extends any[], + F4Result, + F5NextArgs extends any[], + F5Result, + F6NextArgs extends any[], + F6Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => F4Result + ) => (...args: F2NextArgs) => F3Result, + f4: ( + next: (...args: F4NextArgs) => F5Result + ) => (...args: F3NextArgs) => F4Result, + f5: ( + next: (...args: F5NextArgs) => F6Result + ) => (...args: F4NextArgs) => F5Result, + f6: ( + next: (...args: F6NextArgs) => LastResult + ) => (...args: F5NextArgs) => F6Result, + last: (...args: F6NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + F4NextArgs extends any[], + F4Result, + F5NextArgs extends any[], + F5Result, + F6NextArgs extends any[], + F6Result, + F7NextArgs extends any[], + F7Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => F4Result + ) => (...args: F2NextArgs) => F3Result, + f4: ( + next: (...args: F4NextArgs) => F5Result + ) => (...args: F3NextArgs) => F4Result, + f5: ( + next: (...args: F5NextArgs) => F6Result + ) => (...args: F4NextArgs) => F5Result, + f6: ( + next: (...args: F6NextArgs) => F7Result + ) => (...args: F5NextArgs) => F6Result, + f7: ( + next: (...args: F7NextArgs) => LastResult + ) => (...args: F6NextArgs) => F7Result, + last: (...args: F7NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + F4NextArgs extends any[], + F4Result, + F5NextArgs extends any[], + F5Result, + F6NextArgs extends any[], + F6Result, + F7NextArgs extends any[], + F7Result, + F8NextArgs extends any[], + F8Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => F4Result + ) => (...args: F2NextArgs) => F3Result, + f4: ( + next: (...args: F4NextArgs) => F5Result + ) => (...args: F3NextArgs) => F4Result, + f5: ( + next: (...args: F5NextArgs) => F6Result + ) => (...args: F4NextArgs) => F5Result, + f6: ( + next: (...args: F6NextArgs) => F7Result + ) => (...args: F5NextArgs) => F6Result, + f7: ( + next: (...args: F7NextArgs) => LastResult + ) => (...args: F6NextArgs) => F7Result, + f8: ( + next: (...args: F8NextArgs) => LastResult + ) => (...args: F7NextArgs) => F8Result, + last: (...args: F8NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose< + F1Result, + F1Args extends any[], + F1NextArgs extends any[], + F2NextArgs extends any[], + F2Result, + F3NextArgs extends any[], + F3Result, + F4NextArgs extends any[], + F4Result, + F5NextArgs extends any[], + F5Result, + F6NextArgs extends any[], + F6Result, + F7NextArgs extends any[], + F7Result, + F8NextArgs extends any[], + F8Result, + F9NextArgs extends any[], + F9Result, + LastResult +>( + f1: ( + next: (...args: F1NextArgs) => F2Result + ) => (...args: F1Args) => F1Result, + f2: ( + next: (...args: F2NextArgs) => F3Result + ) => (...args: F1NextArgs) => F2Result, + f3: ( + next: (...args: F3NextArgs) => F4Result + ) => (...args: F2NextArgs) => F3Result, + f4: ( + next: (...args: F4NextArgs) => F5Result + ) => (...args: F3NextArgs) => F4Result, + f5: ( + next: (...args: F5NextArgs) => F6Result + ) => (...args: F4NextArgs) => F5Result, + f6: ( + next: (...args: F6NextArgs) => F7Result + ) => (...args: F5NextArgs) => F6Result, + f7: ( + next: (...args: F7NextArgs) => LastResult + ) => (...args: F6NextArgs) => F7Result, + f8: ( + next: (...args: F8NextArgs) => LastResult + ) => (...args: F7NextArgs) => F8Result, + f9: ( + next: (...args: F9NextArgs) => LastResult + ) => (...args: F8NextArgs) => F9Result, + last: (...args: F9NextArgs) => LastResult +): (...args: F1Args) => F1Result + +export function compose(...funcs: ((...args: any[]) => any)[]) { return funcs.reverse().reduce((acc, fn) => fn(acc)) } -export const partial = (fn: Func, ...args: any[]) => { - return (...rest: any[]) => fn(...args, ...rest) -} +/** + * This type produces the type array of TItems with all the type items + * in TItemsToRemove removed from the start of the array type. + * + * @example + * ``` + * RemoveItemsInFront<[number, number], [number]> = [number] + * RemoveItemsInFront<[File, number, string], [File, number]> = [string] + * ``` + */ +type RemoveItemsInFront< + TItems extends any[], + TItemsToRemove extends any[] +> = TItems extends [...TItemsToRemove, ...infer TRest] ? TRest : TItems +export const partial = , R>( + fn: (...args: T) => R, + ...args: TA +) => { + return (...rest: RemoveItemsInFront) => + fn(...([...args, ...rest] as T)) +} /** * Like partial but for unary functions that accept * a single object argument @@ -92,13 +435,13 @@ export const proxied = ( type Cache = Record -const memoize = ( - cache: Cache, - func: Func, - keyFunc: Func | null, +const memoize = ( + cache: Cache, + func: (...args: TArgs) => TResult, + keyFunc: ((...args: TArgs) => string) | null, ttl: number | null ) => { - return function callWithMemo(...args: any): T { + return function callWithMemo(...args: any): TResult { const key = keyFunc ? keyFunc(...args) : JSON.stringify({ args }) const existing = cache[key] if (existing !== undefined) { @@ -123,14 +466,16 @@ const memoize = ( * is given previously computed values will be checked * for expiration before being returned. */ -export const memo = any>( - func: TFunc, +export const memo = ( + func: (...args: TArgs) => any, options: { - key?: Func + key?: (...args: TArgs) => string ttl?: number } = {} ) => { - return memoize({}, func, options.key ?? null, options.ttl ?? null) as TFunc + return memoize({}, func, options.key ?? null, options.ttl ?? null) as ( + ...args: TArgs + ) => any } export type DebounceFunction = { diff --git a/src/tests/curry.test.ts b/src/tests/curry.test.ts index fdbd446d..a5bbdd75 100644 --- a/src/tests/curry.test.ts +++ b/src/tests/curry.test.ts @@ -5,13 +5,15 @@ import type { DebounceFunction } from '../curry' describe('curry module', () => { describe('compose function', () => { test('composes functions', () => { - const useZero = (fn: any) => () => fn(0) - const objectize = (fn: any) => (num: any) => fn({ num }) + const useZero = (fn: (num: number) => number) => () => fn(0) + const objectize = + (fn: (obj: { num: number }) => number) => (num: number) => + fn({ num }) const increment = - (fn: any) => - ({ num }: any) => + (fn: (arg: { num: number }) => number) => + ({ num }: { num: number }) => fn({ num: num + 1 }) - const returnArg = (arg: any) => (args: any) => args[arg] + const returnArg = (arg: 'num') => (args: { num: number }) => args[arg] const composed = _.compose( useZero, @@ -29,15 +31,21 @@ describe('curry module', () => { const result = composed() assert.equal(result, expected) + assert.equal(result, 2) }) test('composes async function', async () => { - const useZero = (fn: any) => async () => await fn(0) - const objectize = (fn: any) => async (num: any) => await fn({ num }) + const useZero = (fn: (num: number) => Promise) => async () => + fn(0) + const objectize = + (fn: (obj: { num: number }) => Promise) => + async (num: number) => + fn({ num }) const increment = - (fn: any) => - async ({ num }: any) => - await fn({ num: num + 1 }) - const returnArg = (arg: any) => async (args: any) => await args[arg] + (fn: (arg: { num: number }) => Promise) => + async ({ num }: { num: number }) => + fn({ num: num + 1 }) + const returnArg = (arg: 'num') => async (args: { num: number }) => + args[arg] const composed = _.compose( useZero, @@ -56,13 +64,110 @@ describe('curry module', () => { assert.equal(result, expected) }) + test('composes function type overloads', () => { + const useZero = (fn: (num: number) => number) => () => fn(0) + const objectize = + (fn: (obj: { num: number }) => number) => (num: number) => + fn({ num }) + const increment = + (fn: (arg: { num: number }) => number) => + ({ num }: { num: number }) => + fn({ num: num + 1 }) + const returnArg = (arg: 'num') => (args: { num: number }) => args[arg] + const returnNum = () => (num: number) => num + + assert.equal(_.compose(useZero, returnNum())(), 0) + + assert.equal(_.compose(useZero, objectize, returnArg('num'))(), 0) + + assert.equal( + _.compose(useZero, objectize, increment, returnArg('num'))(), + 1 + ) + + assert.equal( + _.compose(useZero, objectize, increment, increment, returnArg('num'))(), + 2 + ) + + assert.equal( + _.compose( + useZero, + objectize, + increment, + increment, + increment, + returnArg('num') + )(), + 3 + ) + + assert.equal( + _.compose( + useZero, + objectize, + increment, + increment, + increment, + increment, + returnArg('num') + )(), + 4 + ) + + assert.equal( + _.compose( + useZero, + objectize, + increment, + increment, + increment, + increment, + increment, + returnArg('num') + )(), + 5 + ) + + assert.equal( + _.compose( + useZero, + objectize, + increment, + increment, + increment, + increment, + increment, + increment, + returnArg('num') + )(), + 6 + ) + + assert.equal( + _.compose( + useZero, + objectize, + increment, + increment, + increment, + increment, + increment, + increment, + increment, + returnArg('num') + )(), + 7 + ) + }) }) describe('partial function', () => { test('passes single args', () => { const add = (a: number, b: number) => a + b const expected = 20 - const result = _.partial(add, 10)(10) + const partialed = _.partial(add, 10) + const result = partialed(10) assert.equal(result, expected) }) test('passes many args', () => { @@ -90,11 +195,11 @@ describe('curry module', () => { describe('chain function', () => { test('calls all given functions', () => { - const genesis = () => 0 + const genesis = (num: number, name: string) => 0 const addFive = (num: number) => num + 5 const twoX = (num: number) => num * 2 const func = _.chain(genesis, addFive, twoX) - const result = func(0) + const result = func(0, '') assert.equal(result, 10) }) @@ -138,7 +243,7 @@ describe('curry module', () => { const upperCase: (x: string) => Uppercase = (text: string) => text.toUpperCase() as Uppercase - const getUpperName = _.chain>(getName, upperCase) + const getUpperName = _.chain(getName, upperCase) const result = users.map(getUpperName) assert.deepEqual(result, ['JOHN DOE', 'JOHN SMITH', 'JOHN WICK']) }) @@ -167,17 +272,17 @@ describe('curry module', () => { }) test('uses key to identify unique calls', () => { const func = _.memo( - ({ id }: { id: string }) => { + (arg: { user: { id: string } }) => { const ts = new Date().getTime() - return `${ts}::${id}` + return `${ts}::${arg.user.id}` }, { - key: ({ id }: { id: string }) => id + key: arg => arg.user.id } ) - const resultA = func({ id: 'alpha' }) - const resultB = func({ id: 'beta' }) - const resultA2 = func({ id: 'alpha' }) + const resultA = func({ user: { id: 'alpha' } }) + const resultB = func({ user: { id: 'beta' } }) + const resultA2 = func({ user: { id: 'alpha' } }) assert.equal(resultA, resultA2) assert.notEqual(resultB, resultA) })