|
/** |
|
* FUNCTIONAL COMPOSITION |
|
* with static typing in TypeScript |
|
* |
|
* Tomasz Ducin |
|
* http://ducin.it |
|
* twitter: @tomasz_ducin |
|
*/ |
|
|
|
import { Developer, Nationality } from './types' |
|
|
|
declare const devs: Developer[] |
|
|
|
console.log(devs.length) |
|
|
|
// 1. jumping over a fn signature (fn -> fn) |
|
// 2. pipe/compose |
|
|
|
type ConditionFn<T> = (t: T) => boolean |
|
type DeveloperConditionFn = ConditionFn<Developer> |
|
|
|
const earnsALot = (d: Developer) => d.salary > 8000 |
|
const earnsCrap: DeveloperConditionFn = (d) => d.salary < 2000 |
|
const knowsJavaScript: DeveloperConditionFn = (d: Developer) => d.skills.includes('JavaScript') |
|
|
|
const hasNationality = (n: Nationality): DeveloperConditionFn => |
|
(d: Developer) => |
|
d.nationality === n |
|
|
|
const all = <T>(...fns: ConditionFn<T>[]): ConditionFn<T> => |
|
(t: T) => !fns.find(cfn => !cfn(t)) |
|
|
|
const candidateCriteriaMet = all( |
|
earnsALot, |
|
knowsJavaScript, |
|
hasNationality('PL') |
|
) |
|
|
|
// or |
|
// atLeast |
|
|
|
type ComparatorFn<T> = (e1: T, e2: T) => number |
|
|
|
const earnsMoreThan: ComparatorFn<Developer> = (d1, d2) => d1.salary - d2.salary |
|
|
|
const reverse = <T>(cfn: ComparatorFn<T>) => |
|
(e1: T, e2: T) => -cfn(e1, e2) |
|
|
|
const earnsLessThan = reverse(earnsMoreThan) |
|
const isYoungerThan: ComparatorFn<Developer> = (d1, d2) => d2.personalInfo.age - d1.personalInfo.age |
|
|
|
const combinedSort = <T>(...cfns: ComparatorFn<T>[]): ComparatorFn<T> => |
|
(e1, e2) => { |
|
const firstFn = cfns.find(cfn => !!cfn(e1, e2)) |
|
return firstFn ? firstFn(e1, e2) : 0 |
|
} |
|
|
|
const theOrderOfGod = combinedSort( |
|
isYoungerThan, |
|
reverse(earnsMoreThan), |
|
) |
|
|
|
// console.log(devs.filter(candidateCriteriaMet).length ) |
|
console.log(devs.sort(theOrderOfGod)[0].personalInfo.age ) |
|
|
|
function pipe<T, U, V>( |
|
f: (t: T) => U, |
|
g: (u: U) => V |
|
): (t: T) => V |
|
function pipe<T, U, V, W>( |
|
f: (t: T) => U, |
|
g: (u: U) => V, |
|
h: (v: V) => W, |
|
): (t: T) => W |
|
function pipe<T, U, V, W, X>(f: (t: T) => U, g: (u: U) => V, h: (v: V) => W, i: (w: W) => X): (t: T) => X |
|
function pipe (...fns: Function[]) { |
|
return (value) => fns.reduce((current, fn) => fn(current), value ) |
|
} |
|
|
|
const filter = <T>(cfn: ConditionFn<T>) => |
|
(collection: T[]) => |
|
collection.filter(cfn) |
|
|
|
const getEasterBonus = pipe( |
|
filter(all( |
|
earnsCrap, |
|
hasNationality('PL') |
|
)), |
|
devs => devs.map(d => d.salary / 4), |
|
amounts => amounts.reduce((sum, n) => sum + n) |
|
) |
|
console.log('bonus!', getEasterBonus(devs)) |