- snippets from live coding session
- Tomasz Ducin, @tomasz_ducin
- Wrocław TypeScript #3, 2019.03.27
-
-
Save ducin/f6ceceea520fb57fb2bfd81bd12ae381 to your computer and use it in GitHub Desktop.
Functional Composition with Static Types in TypeScript, Tomasz Ducin, Wrocław TypeScript #3, 2019.03.27
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{ | |
"compilerOptions": { | |
"lib": ["es2018"] | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type Nationality = "US" | "UK" | "FR" | "DE" | "NL" | "PL" | "IT" | "ES"; | |
export type ContractType = "contract" | "permanent"; | |
export type Developer = { | |
"id": number; | |
"nationality": Nationality, | |
"salary": number; | |
"office": [string, string]; | |
"firstName": string; | |
"lastName": string; | |
"title": string; | |
"contractType": ContractType; | |
"email": string; | |
"hiredAt": string; | |
"expiresAt": string; | |
"personalInfo": { | |
"age": number; | |
"email": string; | |
"dateOfBirth": string; | |
}, | |
"skills": string[]; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment