├── sundryTests ├── sum.ts ├── sum.test.ts ├── factorial.test.ts └── factorial.ts ├── codeForChapters ├── chapter 04 │ ├── area.ts │ ├── tricky.ts │ ├── answers │ │ ├── question_04_shuffling_by_sorting.ts │ │ ├── question_04_a_shuffle_test.test.ts │ │ ├── question_04_tuples_to_go.ts │ │ ├── question_04_go_for_a_closure.ts │ │ └── question_04_tuples_to_go.test.ts │ ├── shuffle.ts │ ├── area.test.ts │ ├── isOldEnough.test.ts │ ├── sum3.trick.ts │ ├── maxStrings.ts │ ├── shuffle.test.ts │ ├── isOldEnough.ts │ ├── calculateDebt.js │ ├── fibonacci.ts │ ├── roundFix.test.ts │ └── roundFix.ts ├── chapter 07 │ ├── flip2.ts │ ├── answers │ │ ├── chapter_07_sum_as_you_will.ts │ │ ├── chapter_07_mystery_questions_function.js │ │ ├── chapter_07_currying_by_prototype.ts │ │ ├── chapter_07_questions.js │ │ ├── chapter_07_curry_with_eval_JS.js │ │ ├── chapter_07_uncurrying_the_currying.mjs │ │ ├── chapter_07_shorter_typing.ts │ │ ├── chapter_07_counting_arguments.ts │ │ ├── chapter_07_curry_with_eval.ts │ │ └── chapter_07_working_stylishly.ts │ ├── partial.examples.ts │ ├── curry.test.ts │ ├── curry.examples.ts │ ├── js_versions │ │ ├── partialCurry.js │ │ ├── partial.js │ │ └── curry.js │ ├── partialCurry.examples.ts │ ├── curry.ts │ ├── validation.ts │ └── partialCurry.ts ├── common.ts ├── chapter 05 │ ├── range.ts │ ├── js_versions │ │ └── map.js │ ├── workers │ │ ├── web_fib_worker.js │ │ ├── web_fib_worker.ts │ │ ├── fib_worker.ts │ │ ├── fib_worker_test.ts │ │ ├── random_worker.ts │ │ ├── fib_worker.js │ │ ├── test_worker_1.js │ │ ├── fib_worker_test_with_promise.mjs │ │ ├── fib_worker_test_with_promise.ts │ │ ├── test_worker_1.html │ │ ├── test_worker_1.ts │ │ ├── test_worker_2.html │ │ ├── test_worker_2.js │ │ ├── test_worker_2.ts │ │ ├── pool.ts │ │ └── pool_test.ts │ ├── answers │ │ ├── question_5.old_style_code_only.ts │ │ ├── question_5.reversed_reverse.ts │ │ ├── question_5.missing_equivalents.test.ts │ │ ├── question_5.more_formal_testing.test.ts │ │ ├── question_5.missing_equivalents.ts │ │ ├── question_5.range_generator.test.ts │ │ ├── question_5.producing_a_csv.ts │ │ ├── question_5.producing_better_output.ts │ │ ├── question_5.ranging_far_and_wide.test.ts │ │ ├── question_5.ranging_far_and_wide.ts │ │ ├── question_5.reverse_by_summing.ts │ │ ├── question_5.wishful_thinking.ts │ │ ├── question_5.generating_html.ts │ │ ├── question_5.emptying_the_pool.ts │ │ └── question_5.range_generator.ts │ ├── map.ts │ ├── loops.ts │ ├── reverse.ts │ ├── sum.ts │ ├── map.examples.ts │ ├── copy.ts │ ├── range.examples.ts │ ├── filter.ts │ ├── search.ts │ └── flat.ts ├── chapter 06 │ ├── answers │ │ ├── question_06_a_border_case.ts │ │ ├── question_06_not_reinventing_the_wheel.ts │ │ ├── question_06_not_in_ts.ts │ │ ├── question_06_go_with_arrows.ts │ │ ├── question_06_just_say_no.ts │ │ ├── question_06_typed_demethodizing.ts │ │ ├── question_06_many_arities.ts │ │ ├── question_06_wrong_function_length.ts │ │ ├── question_06_invert_tests.test.ts │ │ ├── question_06_randomizing_balancer.ts │ │ ├── question_06_throttling_promises.ts │ │ ├── question_06_mapping_for_memory.ts │ │ └── question_06_missing_companion.ts │ ├── getField.ts │ ├── fibonacci.ts │ ├── invert.ts │ ├── debounce.ts │ ├── arity.examples.ts │ ├── promisify.ts │ ├── throttle.ts │ ├── once.examples.ts │ ├── not.ts │ ├── methodize.ts │ ├── invert.examples.ts │ ├── demethodize.ts │ ├── arity.ts │ ├── timing.examples.ts │ ├── promisify.examples.ts │ ├── timing.ts │ ├── logging.examples.ts │ ├── memoize.examples.ts │ ├── demethodize.examples.ts │ ├── methodize.examples.ts │ ├── logging3.test.ts │ ├── not.examples.ts │ ├── js_versions │ │ └── demethodize.js │ ├── logging.ts │ ├── logging.test.ts │ ├── binaryOp.ts │ ├── logging3.ts │ ├── once.ts │ └── getField.examples.ts ├── chapter 08 │ ├── transducer_comparisons.zip │ ├── transducer_comparisons │ │ ├── 1st Run.png │ │ ├── 2nd Run.png │ │ ├── 3rd Run.png │ │ ├── 4th run.png │ │ └── 5th run.png │ ├── pipeline.proposal.ts │ ├── pointfree.ts │ ├── answers │ │ ├── chapter_08_headline_capitalization.ts │ │ └── chapter_08_reverse_typing.ts │ ├── transducing.ts │ ├── chaining.examples.ts │ ├── pipetwo.test.ts │ ├── chaining.test.ts │ ├── chaining.ts │ └── transducing.examples.ts ├── chapter 09 │ ├── answers │ │ ├── chapter_09_into_reverse.ts │ │ ├── chapter_09_more_efficiency.ts │ │ ├── chapter_09_sorting_recursively.ts │ │ ├── chapter_09_what_could_go_wrong.ts │ │ ├── chapter_09_mutual_problem.ts │ │ ├── chapter_09_completing_callbacks.ts │ │ ├── chapter_09_longest_common_subsequence.ts │ │ ├── chapter_09_odds_evens_trampolining.ts │ │ ├── chapter_09_symmetrical_queens.ts │ │ ├── chapter_09_recursive_logic.ts │ │ └── chapter_09_at_odds_with_javascript.ts │ ├── power.ts │ ├── quicksort.ts │ ├── trampoline.ts │ ├── find.ts │ ├── directory.ts │ ├── filter.ts │ ├── hanoi.ts │ ├── reduce.ts │ ├── search.ts │ ├── mutual.ts │ ├── pipeline.ts │ ├── loops.ts │ ├── queens.ts │ ├── trampoline.examples.ts │ ├── makeChange.ts │ ├── tailRecursion.ts │ └── dom.ts ├── chapter 10 │ ├── lists.ts │ ├── deepFreeze.ts │ ├── answers │ │ ├── chapter_10_lenses_for_arrays.mjs │ │ ├── chapter_10_lenses_into_maps.ts │ │ ├── chapter_10_not_just_date_problems.ts │ │ ├── chapter_10_the_mote.ts │ │ ├── chapter_10_composing_many_lenses.ts │ │ ├── chapter_10_freezing_by_proxying.ts │ │ ├── chapter_10_going_in_circles.ts │ │ ├── chapter_10_inserting_into_a_list.ts │ │ └── chapter_10_accessing_virtual_attributes.ts │ ├── prisms.ts │ ├── js_versions │ │ └── prisms.mjs │ ├── deepCopy.ts │ ├── getByPath.ts │ ├── getByPath.examples.ts │ ├── maxStrings.ts │ ├── setByPath.examples.ts │ ├── deepFreeze.examples.ts │ ├── deepCopy.examples.ts │ ├── setByPath.ts │ └── lensesWithFunctions.examples.ts ├── chapter 12 │ ├── functor.examples.ts │ ├── container.examples.ts │ ├── functor.ts │ ├── try.examples.ts │ ├── monad.examples.ts │ ├── container.ts │ ├── try.ts │ ├── either.ts │ ├── answers │ │ ├── chapter_12_code_shortening.ts │ │ └── chapter_12_extending_prototypes.ts │ ├── monad.ts │ ├── monadsMadeWithFunctions.examples.use.mjs │ ├── maybe.ts │ ├── maybe.examples.ts │ └── monadsMadeWithPromises.new.mjs ├── chapter 01 │ ├── closure.js │ ├── answers │ │ ├── question_01_code_squeezing.ts │ │ ├── question_01_factorial_errors.ts │ │ ├── question_01_climbing_factorial.ts │ │ ├── question_01_classes_as_1st_class.ts │ │ ├── question_01_typescript_please.ts │ │ └── question_01_factorial_testing.test.ts │ ├── declarative.js │ ├── imperative.js │ ├── factorial.js │ ├── factorial.ts │ └── spread.js ├── chapter 02 │ ├── once_JS.js │ ├── answers │ │ ├── question_02_no_extra_variables.ts │ │ ├── question_02_alternating_fns.ts │ │ ├── question_02_alternating_fns.manual.ts │ │ ├── question_02_everything_has_a_limit.ts │ │ ├── question_02_allow_for_crashing.ts │ │ ├── question_02_everything_has_a_limit.manual.ts │ │ ├── question_02_allow_for_crashing.manual.ts │ │ ├── question_02_everything_has_a_limit.test.ts │ │ ├── question_02_say_no_to_arrows.ts │ │ ├── question_02_alternating_fns.test.ts │ │ └── question_02_allow_for_crashing.test.ts │ ├── onceAndAfter.ts │ ├── onceAndAfter.manual.ts │ ├── once.manual.ts │ ├── onceAndAfter.test.ts │ ├── once.ts │ └── once.test.ts ├── chapter 03 │ ├── myCounter.ts │ ├── ajax.module.ts │ ├── sum3.ts │ ├── showItself.js │ ├── ajax.ts │ ├── sort.ts │ └── answers │ │ └── question_03_bindless_binding.ts └── chapter 11 │ ├── simpleAjax2.mjs │ ├── decorator1.html │ ├── findRoute.js │ ├── simpleAjax1.mjs │ ├── decorator2.html │ └── chapter_11_reactive copy.html ├── .babelrc.json ├── babel.config.json ├── .prettierrc.js ├── .eslintrc.js ├── .gitignore ├── LICENSE └── package.json /sundryTests/sum.ts: -------------------------------------------------------------------------------- 1 | const sum = (a: number, b: number) => a + b; 2 | 3 | export { sum }; 4 | -------------------------------------------------------------------------------- /sundryTests/sum.test.ts: -------------------------------------------------------------------------------- 1 | import { sum } from "./sum"; 2 | 3 | test("2+2=4", () => { 4 | expect(sum(2, 2)).toBe(4); 5 | }); 6 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/area.ts: -------------------------------------------------------------------------------- 1 | const PI = 3.14159265358979; 2 | 3 | const circleArea = (r: number) => PI * r ** 2; 4 | 5 | export { circleArea }; 6 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/flip2.ts: -------------------------------------------------------------------------------- 1 | function flip2(fn: (a: A, b: B) => R) { 2 | return (p1: B, p2: A) => fn(p2, p1); 3 | } 4 | 5 | export { flip2 }; 6 | -------------------------------------------------------------------------------- /codeForChapters/common.ts: -------------------------------------------------------------------------------- 1 | export type FN = (..._args: any[]) => any; 2 | 3 | export type OBJ = { [key: string]: any }; 4 | 5 | export type OPT = X | undefined | null; 6 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/range.ts: -------------------------------------------------------------------------------- 1 | const range = (start: number, stop: number): number[] => 2 | new Array(stop - start).fill(0).map((v, i) => start + i); 3 | 4 | export { range }; 5 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/js_versions/map.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | 3 | const myMap = (arr, fn) => 4 | arr.reduce((x, y) => x.concat(fn(y)), []); 5 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/web_fib_worker.js: -------------------------------------------------------------------------------- 1 | function fib(n) { 2 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 3 | } 4 | onmessage = function (e) { 5 | return postMessage(fib(e.data)); 6 | }; 7 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_a_border_case.ts: -------------------------------------------------------------------------------- 1 | const getField = 2 | (f: keyof D) => 3 | (obj: D) => 4 | obj[f]; 5 | 6 | console.log(getField("someField")(null)); // rejected! 7 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducer_comparisons.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Functional-Programming-3E/HEAD/codeForChapters/chapter 08/transducer_comparisons.zip -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.old_style_code_only.ts: -------------------------------------------------------------------------------- 1 | import { gettysburg } from "../flat"; 2 | 3 | const words = gettysburg.join(" ").split(" ").length; 4 | 5 | console.log(words); // again 270 6 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/map.ts: -------------------------------------------------------------------------------- 1 | const myMap = (arr: T[], fn: (x: T) => R): R[] => 2 | arr.reduce( 3 | (x: R[], y: T): R[] => x.concat([fn(y)]), 4 | [] as R[] 5 | ); 6 | 7 | export { myMap }; 8 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/tricky.ts: -------------------------------------------------------------------------------- 1 | let mult = 1; 2 | const f = (x: number): number => { 3 | mult = -mult; 4 | return x * mult; 5 | }; 6 | 7 | console.log(f(2) + f(5)); // 3 8 | console.log(f(5) + f(2)); // -3 9 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/getField.ts: -------------------------------------------------------------------------------- 1 | /* 2 | const getField = f => obj => obj[f]; 3 | */ 4 | 5 | const getField = 6 | (f: keyof D) => 7 | (obj: D) => 8 | obj[f]; 9 | 10 | export { getField }; 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducer_comparisons/1st Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Functional-Programming-3E/HEAD/codeForChapters/chapter 08/transducer_comparisons/1st Run.png -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducer_comparisons/2nd Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Functional-Programming-3E/HEAD/codeForChapters/chapter 08/transducer_comparisons/2nd Run.png -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducer_comparisons/3rd Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Functional-Programming-3E/HEAD/codeForChapters/chapter 08/transducer_comparisons/3rd Run.png -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducer_comparisons/4th run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Functional-Programming-3E/HEAD/codeForChapters/chapter 08/transducer_comparisons/4th run.png -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducer_comparisons/5th run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-JavaScript-Functional-Programming-3E/HEAD/codeForChapters/chapter 08/transducer_comparisons/5th run.png -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/web_fib_worker.ts: -------------------------------------------------------------------------------- 1 | function fib(n: number): number { 2 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 3 | } 4 | 5 | onmessage = (e: MessageEvent) => 6 | postMessage(fib(e.data)); 7 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_into_reverse.ts: -------------------------------------------------------------------------------- 1 | const reverse = (str: string): string => 2 | str.length === 0 ? "" : reverse(str.slice(1)) + str[0]; 3 | 4 | console.log(reverse("MONTEVIDEO")); 5 | 6 | export {}; 7 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_not_reinventing_the_wheel.ts: -------------------------------------------------------------------------------- 1 | const max = (...arr: number[]): number => Math.max(...arr); 2 | 3 | const min = (...arr: number[]): number => Math.min(...arr); 4 | 5 | export { min, max }; 6 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.reversed_reverse.ts: -------------------------------------------------------------------------------- 1 | const reversedReverse = (str: string): string => 2 | str.split("").reduceRight((x, y) => y + x, ""); 3 | 4 | console.log(reversedReverse("URUGUAY")); 5 | 6 | export {}; 7 | -------------------------------------------------------------------------------- /sundryTests/factorial.test.ts: -------------------------------------------------------------------------------- 1 | import { factorial } from "./factorial"; 2 | 3 | test("5! = 120", () => { 4 | expect(factorial(5)).toBe(120); 5 | }); 6 | 7 | test("0! = 1", () => { 8 | expect(factorial(0)).toBe(1); 9 | }); 10 | -------------------------------------------------------------------------------- /.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-proposal-pipeline-operator"], 3 | "presets": [ 4 | [ 5 | "@babel/preset-env", 6 | { "targets": { "node": "current" } } 7 | ], 8 | "@babel/preset-typescript" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-proposal-pipeline-operator"], 3 | "presets": [ 4 | [ 5 | "@babel/preset-env", 6 | { "targets": { "node": "current" } } 7 | ], 8 | "@babel/preset-typescript" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/fibonacci.ts: -------------------------------------------------------------------------------- 1 | function fib(n: number): number { 2 | if (n == 0) { 3 | return 0; 4 | } else if (n == 1) { 5 | return 1; 6 | } else { 7 | return fib(n - 2) + fib(n - 1); 8 | } 9 | } 10 | 11 | export { fib }; 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/lists.ts: -------------------------------------------------------------------------------- 1 | class ListNode { 2 | value: T; 3 | next: ListNode | null; 4 | 5 | constructor(value: any, next = null) { 6 | this.value = value; 7 | this.next = next; 8 | } 9 | } 10 | 11 | export { ListNode }; 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/loops.ts: -------------------------------------------------------------------------------- 1 | import { range } from "./range"; 2 | 3 | const fact4 = (n: number): number => { 4 | let result = 1; 5 | range(1, n + 1).forEach((v) => (result *= v)); 6 | return result; 7 | }; 8 | 9 | console.log(fact4(5)); // 120 10 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/answers/question_04_shuffling_by_sorting.ts: -------------------------------------------------------------------------------- 1 | const sortingShuffle = (arr: T[]): T[] => 2 | arr 3 | .map((v) => ({ val: v, key: Math.random() })) 4 | .sort((a, b) => a.key - b.key) 5 | .map((o) => o.val); 6 | 7 | export { sortingShuffle }; 8 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/invert.ts: -------------------------------------------------------------------------------- 1 | /* 2 | const invert = (fn) => (...args) => -fn(...args); 3 | */ 4 | 5 | const invert = 6 | number>(fn: T) => 7 | (...args: Parameters): number => 8 | -fn(...args); 9 | 10 | export { invert }; 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/functor.examples.ts: -------------------------------------------------------------------------------- 1 | import { Functor } from "./functor"; 2 | 3 | const num1 = new Functor(22); 4 | console.log(num1.toString()); 5 | 6 | const num2 = num1.map((x: number) => 10 * x + 9); 7 | console.log(num2.toString()); 8 | 9 | export { Functor }; 10 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/closure.js: -------------------------------------------------------------------------------- 1 | function newCounter() { 2 | let count = 0; 3 | return function () { 4 | count++; 5 | return count; 6 | }; 7 | } 8 | 9 | const nc = newCounter(); 10 | console.log(nc()); // 1 11 | console.log(nc()); // 2 12 | console.log(nc()); // 3 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/container.examples.ts: -------------------------------------------------------------------------------- 1 | import { Container } from "./container"; 2 | 3 | const num1 = new Container(22); 4 | console.log(num1.map((x: number): number => x)); 5 | console.log(num1.toString()); 6 | 7 | const num2 = Container.of("FK"); 8 | console.log(num2.toString()); 9 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/once_JS.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | 3 | const onceJS = (fn) => { 4 | let done = false; 5 | 6 | return (...args) => { 7 | if (!done) { 8 | done = true; 9 | return fn(...args); 10 | } 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/fib_worker.ts: -------------------------------------------------------------------------------- 1 | import { parentPort } from "worker_threads"; 2 | 3 | function fib(n: number): number { 4 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 5 | } 6 | 7 | parentPort!.on("message", (m: number) => 8 | parentPort!.postMessage(fib(m)) 9 | ); 10 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_sum_as_you_will.ts: -------------------------------------------------------------------------------- 1 | const sumMany = (total: number) => (value?: number) => 2 | value === undefined ? total : sumMany(total + value); 3 | 4 | // @ts-expect-error TS cannot figure types out here... 5 | console.log(sumMany(2)(2)(9)(6)(0)(-3)()); // 16 6 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_no_extra_variables.ts: -------------------------------------------------------------------------------- 1 | const once = any>( 2 | fn: FNType | null 3 | ) => 4 | ((...args: Parameters) => { 5 | fn && fn(...args); 6 | fn = null; 7 | }) as FNType; 8 | 9 | export { once }; 10 | -------------------------------------------------------------------------------- /sundryTests/factorial.ts: -------------------------------------------------------------------------------- 1 | const factorial = (n: number): number => { 2 | if (n < 0) { 3 | throw new Error("Cannot do factorial if negative"); 4 | } else if (n === 0) { 5 | return 1; 6 | } else { 7 | return n * factorial(n - 1); 8 | } 9 | }; 10 | 11 | export { factorial }; 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_mystery_questions_function.js: -------------------------------------------------------------------------------- 1 | const partial = 2 | (fn) => 3 | (...params) => 4 | fn.length <= params.length 5 | ? fn(...params) 6 | : (...otherParams) => 7 | partial(fn)(...params, ...otherParams); 8 | 9 | export { partial }; 10 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | // prettier.config.js or .prettierrc.js 2 | module.exports = { 3 | arrowParens: "always", 4 | bracketSpacing: true, 5 | printWidth: 60, 6 | quoteProps: "as-needed", 7 | semi: true, 8 | singleQuote: false, 9 | tabWidth: 2, 10 | trailingComma: "es5", 11 | useTabs: false, 12 | }; 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_alternating_fns.ts: -------------------------------------------------------------------------------- 1 | const alternator = any>( 2 | fn1: FNType, 3 | fn2: FNType 4 | ) => 5 | ((...args: Parameters) => { 6 | [fn1, fn2] = [fn2, fn1]; 7 | return fn2(...args); 8 | }) as FNType; 9 | 10 | export { alternator }; 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/functor.ts: -------------------------------------------------------------------------------- 1 | import { Container } from "./container"; 2 | 3 | class Functor extends Container { 4 | static of(x: B) { 5 | return new Functor(x); 6 | } 7 | 8 | map(fn: (_: A) => B): Functor { 9 | return Functor.of(fn(this.x)); 10 | } 11 | } 12 | 13 | export { Functor }; 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/myCounter.ts: -------------------------------------------------------------------------------- 1 | const myCounter = (function myCounter(initialValue = 0) { 2 | let count = initialValue; 3 | return function () { 4 | count++; 5 | return count; 6 | }; 7 | })(77); 8 | 9 | console.log(myCounter()); // 78 10 | console.log(myCounter()); // 79 11 | console.log(myCounter()); // 80 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/partial.examples.ts: -------------------------------------------------------------------------------- 1 | import { partial } from "./partial"; 2 | 3 | const make3 = (a: string, b: number, c: string): string => 4 | `${a}:${b}:${c}`; 5 | 6 | const f0 = partial(make3); 7 | const f1 = f0(undefined, 2); 8 | const f2 = f1("A"); 9 | const f3 = f2("Z"); 10 | console.log(f3); 11 | 12 | export {}; 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_currying_by_prototype.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Function { 3 | curry(): (...args: any) => any; 4 | } 5 | } 6 | 7 | Function.prototype.curry = function () { 8 | return this.length === 0 9 | ? this() 10 | : (p) => this.bind(this, p).curry(); 11 | }; 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/fib_worker_test.ts: -------------------------------------------------------------------------------- 1 | import { Worker } from "worker_threads"; 2 | 3 | const worker = new Worker("./fib_worker.js"); 4 | 5 | console.log("START"); 6 | worker.postMessage(40); 7 | console.log("END"); 8 | 9 | worker.on("message", (msg) => { 10 | console.log("MESSAGE", msg); 11 | worker.terminate(); 12 | }); 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/answers/question_01_code_squeezing.ts: -------------------------------------------------------------------------------- 1 | const shorterCounter = () => { 2 | let count = 0; 3 | return () => ++count; 4 | }; 5 | 6 | const counter1 = shorterCounter(); 7 | console.log(counter1()); 8 | console.log(counter1()); 9 | console.log(counter1()); 10 | console.log(counter1()); 11 | /* 12 | 1 13 | 2 14 | 3 15 | 4 16 | */ 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_more_efficiency.ts: -------------------------------------------------------------------------------- 1 | const partition = ( 2 | arr: A[], 3 | fn: (x: A) => boolean 4 | ): [A[], A[]] => 5 | arr.reduce( 6 | (result: [A[], A[]], elem: A) => { 7 | result[fn(elem) ? 0 : 1].push(elem); 8 | return result; 9 | }, 10 | [[], []] 11 | ); 12 | 13 | export { partition }; 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/curry.test.ts: -------------------------------------------------------------------------------- 1 | import { curry, make3 } from "./curry.examples"; 2 | 3 | describe("with curry", function () { 4 | it("you fix arguments one by one", () => { 5 | const make3a = curry(make3); 6 | const make3b = make3a("A")(2); 7 | const make3c = make3b("Z"); 8 | expect(make3c).toBe(make3("A", 2, "Z")); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_alternating_fns.manual.ts: -------------------------------------------------------------------------------- 1 | import { alternator } from "./question_02_alternating_fns"; 2 | 3 | const sayA = () => console.log("A"); 4 | const sayB = () => console.log("B"); 5 | 6 | const alt = alternator(sayA, sayB); 7 | 8 | alt(); // A 9 | alt(); // B 10 | alt(); // A 11 | alt(); // B 12 | alt(); // A 13 | alt(); // B 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_everything_has_a_limit.ts: -------------------------------------------------------------------------------- 1 | const thisManyTimes = 2 | any>( 3 | fn: FNType, 4 | limit: number 5 | ) => 6 | (...args: Parameters) => { 7 | if (limit > 0) { 8 | limit--; 9 | return fn(...args); 10 | } 11 | }; 12 | 13 | export { thisManyTimes }; 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/debounce.ts: -------------------------------------------------------------------------------- 1 | const debounce = void>( 2 | fn: T, 3 | delay = 1000 4 | ) => { 5 | let timer: ReturnType; 6 | return (...args: Parameters): void => { 7 | clearTimeout(timer); 8 | timer = setTimeout(() => fn(...args), delay); 9 | }; 10 | }; 11 | 12 | export { debounce }; 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/random_worker.ts: -------------------------------------------------------------------------------- 1 | import { parentPort } from "worker_threads"; 2 | 3 | async function random(n: number): Promise { 4 | await new Promise((resolve) => setTimeout(resolve, n)); 5 | return Math.floor(n * Math.random()); 6 | } 7 | 8 | parentPort!.on("message", async (m) => 9 | parentPort!.postMessage(await random(m)) 10 | ); 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_not_in_ts.ts: -------------------------------------------------------------------------------- 1 | const not = 2 | boolean>(fn: T) => 3 | (...args: Parameters): boolean => 4 | !fn(...args); 5 | 6 | const invert = 7 | number>(fn: T) => 8 | (...args: Parameters): number => 9 | -fn(...args); 10 | 11 | export { not, invert }; 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/fib_worker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | "use strict"; 4 | exports.__esModule = true; 5 | var worker_threads_1 = require("worker_threads"); 6 | function fib(n) { 7 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 8 | } 9 | worker_threads_1.parentPort.on("message", function (m) { 10 | return worker_threads_1.parentPort.postMessage(fib(m)); 11 | }); 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/power.ts: -------------------------------------------------------------------------------- 1 | const powerN = (base: number, power: number): number => { 2 | if (power === 0) { 3 | return 1; 4 | } else if (power % 2) { 5 | // odd power? 6 | return base * powerN(base, power - 1); 7 | } else { 8 | // even power? 9 | return powerN(base * base, power / 2); 10 | } 11 | }; 12 | 13 | console.log(powerN(2, 13)); 14 | 15 | export {}; 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/try.examples.ts: -------------------------------------------------------------------------------- 1 | import { Try } from "./try"; 2 | import type { OBJ } from "../common"; 3 | 4 | const getField2 = (attr: string) => (obj: OBJ | null) => 5 | new Try(() => obj![attr], "NULL OBJECT"); 6 | 7 | const x = getField2("somefield")(null); 8 | 9 | console.log(x.isLeft()); // true 10 | console.log(x.toString()); // Left(NULL OBJECT) 11 | 12 | export { Try }; 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/declarative.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { name: "John", age: 23, other: "xxx" }, 3 | { name: "Paul", age: 18, other: "yyy" }, 4 | { name: "George", age: 16, other: "zzz" }, 5 | { name: "Ringo", age: 25, other: "ttt" }, 6 | ]; 7 | 8 | // Declarative 9 | 10 | const isAdult = (person) => person.age >= 21; 11 | const result2 = data.filter(isAdult); 12 | console.log(result2); 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/reverse.ts: -------------------------------------------------------------------------------- 1 | const reverseString = (str: string): string => { 2 | const arr = str.split(""); 3 | arr.reverse(); 4 | return arr.join(""); 5 | }; 6 | console.log(reverseString("MONTEVIDEO")); // OEDIVETNOM 7 | 8 | const reverseString2 = (str: string): string => 9 | str.split("").reduceRight((x, y) => x + y, ""); 10 | console.log(reverseString2("OEDIVETNOM")); // MONTEVIDEO 11 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/arity.examples.ts: -------------------------------------------------------------------------------- 1 | import { unary, arity } from "./arity"; 2 | 3 | console.log(["123.45", "-67.8", "90"].map(unary(parseInt))); 4 | // [123, -67, 90] 5 | 6 | console.log( 7 | ["123.45", "-67.8", "90"].map((x) => 8 | parseInt(x, undefined) 9 | ) 10 | ); 11 | 12 | const pp = arity(1, parseInt); 13 | 14 | console.log(["123.45", "-67.8", "90"].map(pp)); 15 | // [123, -67, 90] 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/promisify.ts: -------------------------------------------------------------------------------- 1 | // VARIADIC TYPES 2 | 3 | const promisify = 4 | ( 5 | fn: (...args: [...T, (err: E, data: D) => void]) => void 6 | ) => 7 | (...args: T): Promise => 8 | new Promise((resolve, reject) => 9 | fn(...args, (err: E, data: D) => 10 | err ? reject(err) : resolve(data) 11 | ) 12 | ); 13 | 14 | export { promisify }; 15 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/monad.examples.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable no-unused-vars */ 3 | 4 | import { Monad } from "./monad"; 5 | const add = (x: number) => (y: number) => x + y; // or curry((x,y) => x+y) 6 | const something = Monad.of(2).map(add); 7 | 8 | const monad5 = something.ap(Monad.of(3)); // Monad(5) 9 | console.log(monad5, monad5.toString()); 10 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/onceAndAfter.ts: -------------------------------------------------------------------------------- 1 | const onceAndAfter = < 2 | FNType extends (...args: any[]) => any 3 | >( 4 | f: FNType, 5 | g: FNType 6 | ) => { 7 | let done = false; 8 | 9 | return ((...args: Parameters) => { 10 | if (!done) { 11 | done = true; 12 | return f(...args); 13 | } else { 14 | return g(...args); 15 | } 16 | }) as FNType; 17 | }; 18 | 19 | export { onceAndAfter }; 20 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_questions.js: -------------------------------------------------------------------------------- 1 | const curry = fn => (...args) => 2 | args.length >= fn.length ? fn(...args) : curry(fn.bind(null, ...args)); 3 | 4 | const make3 = (a, b, c) => String(100 * a + 10 * b + c); 5 | const make3curried = curry(make3); 6 | console.log(make3curried(1)(2)(3)); 7 | console.log(make3curried(4, 5)(6)); 8 | console.log(make3curried(7, 8, 9)); 9 | /* 10 | 123 11 | 456 12 | 789 13 | */ 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/throttle.ts: -------------------------------------------------------------------------------- 1 | const throttle = void>( 2 | fn: T, 3 | delay = 1000 4 | ) => { 5 | let timer: ReturnType | undefined; 6 | return (...args: Parameters): void => { 7 | if (!timer) { 8 | timer = setTimeout(() => { 9 | timer = undefined; 10 | }, delay); 11 | fn(...args); 12 | } 13 | }; 14 | }; 15 | 16 | export { throttle }; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/answers/question_04_a_shuffle_test.test.ts: -------------------------------------------------------------------------------- 1 | import { shuffle } from "../shuffle"; 2 | 3 | describe("shuffleTest", function () { 4 | it("doesn't change the array length or elements", () => { 5 | const a = [22, 9, 60, 22, 12, 4, 56, 22, 60]; 6 | const oldA = JSON.stringify([...a].sort()); 7 | shuffle(a); 8 | const newA = JSON.stringify([...a].sort()); 9 | expect(oldA).toBe(newA); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/test_worker_1.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function fib(n) { 3 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 4 | } 5 | function getNumber() { 6 | return Number(document.getElementById("num").value); 7 | } 8 | function showResult(result) { 9 | document.getElementById("res").innerText = String(result); 10 | } 11 | /* eslint-disable-next-line */ 12 | function locally() { 13 | showResult(fib(getNumber())); 14 | } 15 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/deepFreeze.ts: -------------------------------------------------------------------------------- 1 | import type { OBJ } from "../common"; 2 | 3 | const deepFreeze = (obj: O): O => { 4 | if ( 5 | obj && 6 | typeof obj === "object" && 7 | !Object.isFrozen(obj) 8 | ) { 9 | Object.freeze(obj); 10 | Object.getOwnPropertyNames(obj).forEach((prop) => 11 | deepFreeze(obj[prop]) 12 | ); 13 | } 14 | 15 | return obj; 16 | }; 17 | 18 | export { deepFreeze }; 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/onceAndAfter.manual.ts: -------------------------------------------------------------------------------- 1 | import { onceAndAfter } from "./onceAndAfter"; 2 | 3 | const squeak = (x: string) => console.log(x, "squeak!!"); 4 | const creak = (x: string) => console.log(x, "creak!!"); 5 | 6 | const makeSound = onceAndAfter(squeak, creak); 7 | 8 | makeSound("door"); // "door squeak!!" 9 | makeSound("door"); // "door creak!!" 10 | makeSound("door"); // "door creak!!" 11 | makeSound("door"); // "door creak!!" 12 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/curry.examples.ts: -------------------------------------------------------------------------------- 1 | import { curry } from "./curry"; 2 | 3 | const make3 = (a: string, b: number, c: string): string => 4 | `${a}:${b}:${c}`; 5 | const f1 = curry(make3); 6 | // (arg: string) => (arg: number) => (arg: string) => string 7 | const f2 = f1("A"); 8 | // (arg: number) => (arg: string) => string 9 | const f3 = f2(2); 10 | // (arg: string) => string 11 | const f4 = f3("Z"); 12 | // string 13 | console.log(f4); 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_lenses_for_arrays.mjs: -------------------------------------------------------------------------------- 1 | import curry from "../../chapter 07/js_versions/curry.js"; 2 | import lens from "../js_versions/lensesWithObjects.mjs"; 3 | 4 | const getArray = curry((ind, arr) => arr[ind]); 5 | 6 | const setArray = curry((ind, value, arr) => { 7 | arr[ind] = value; 8 | return arr; 9 | }); 10 | 11 | const lensArray = (ind) => 12 | lens(getArray(ind), setArray(ind)); 13 | 14 | export {}; 15 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/imperative.js: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { name: "John", age: 23, other: "xxx" }, 3 | { name: "Paul", age: 18, other: "yyy" }, 4 | { name: "George", age: 16, other: "zzz" }, 5 | { name: "Ringo", age: 25, other: "ttt" }, 6 | ]; 7 | 8 | // Imperative 9 | 10 | const result1 = []; 11 | for (let i = 0; i < data.length; i++) { 12 | if (data[i].age >= 21) { 13 | result1.push(data[i]); 14 | } 15 | } 16 | console.log(result1); 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_lenses_into_maps.ts: -------------------------------------------------------------------------------- 1 | import { lens } from "../lensesWithObjects"; 2 | 3 | const getMap = 4 | (key: K) => 5 | (map: Map) => 6 | map.get(key); 7 | 8 | const setMap = 9 | (key: K) => 10 | (value: V) => 11 | (map: Map) => 12 | new Map(map).set(key, value); 13 | 14 | const lensMap = (key: K) => 15 | lens(getMap(key), setMap(key)); 16 | 17 | export {}; 18 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/prisms.ts: -------------------------------------------------------------------------------- 1 | import type { OBJ } from "../common"; 2 | 3 | const getFieldP = 4 | (attr: string) => 5 | (obj: O) => 6 | obj && attr in obj ? obj[attr] : undefined; 7 | 8 | const setFieldP = 9 | (attr: string) => 10 | (value: any) => 11 | (obj: O): O => 12 | obj && attr in obj 13 | ? { ...obj, [attr]: value } 14 | : { ...obj }; 15 | 16 | export { getFieldP, setFieldP }; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/shuffle.ts: -------------------------------------------------------------------------------- 1 | const shuffle = (arr: T[]): T[] => { 2 | const len = arr.length; 3 | for (let i = 0; i < len - 1; i++) { 4 | const r = Math.floor(Math.random() * (len - i)); 5 | [arr[i], arr[i + r]] = [arr[i + r], arr[i]]; 6 | } 7 | return arr; 8 | }; 9 | 10 | /* 11 | const xxx = [11, 22, 33, 44, 55, 66, 77, 88]; 12 | console.log(shuffle(xxx)); 13 | // [55, 77, 88, 44, 33, 11, 66, 22] 14 | */ 15 | 16 | export { shuffle }; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/answers/question_01_factorial_errors.ts: -------------------------------------------------------------------------------- 1 | const carefulFact = (n: number): number | never => { 2 | if ( 3 | typeof n === "number" && 4 | n >= 0 && 5 | n === Math.floor(n) 6 | ) { 7 | const innerFact = (n: number): number => 8 | n === 0 ? 1 : n * innerFact(n - 1); 9 | return innerFact(n); 10 | } else { 11 | throw new Error("Wrong parameter for carefulFact2"); 12 | } 13 | }; 14 | 15 | export { carefulFact }; 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/once.manual.ts: -------------------------------------------------------------------------------- 1 | import { once } from "./once"; 2 | 3 | const squeak = (a: string) => console.log(a, " squeak!!"); 4 | 5 | squeak("original"); // "original squeak!!" 6 | squeak("original"); // "original squeak!!" 7 | squeak("original"); // "original squeak!!" 8 | 9 | const squeakOnce = once(squeak); 10 | 11 | squeakOnce("only once"); // "only once squeak!!" 12 | squeakOnce("only once"); // no output 13 | squeakOnce("only once"); // no output 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/area.test.ts: -------------------------------------------------------------------------------- 1 | import { circleArea } from "./area"; 2 | 3 | describe("circle area", function () { 4 | it("is zero for radius 0", () => { 5 | const area = circleArea(0); 6 | expect(area).toBe(0); 7 | }); 8 | 9 | it("is PI for radius 1", () => { 10 | expect(circleArea(1)).toBeCloseTo(Math.PI); 11 | }); 12 | 13 | it("is approximately 12.5664 for radius 2", () => 14 | expect(circleArea(2)).toBeCloseTo(12.5664)); 15 | }); 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/fib_worker_test_with_promise.mjs: -------------------------------------------------------------------------------- 1 | import { Worker } from "worker_threads"; 2 | 3 | const callWorker = (filename, value) => 4 | new Promise((resolve) => { 5 | const worker = new Worker(filename); 6 | worker.on("message", resolve); 7 | worker.postMessage(value); 8 | }); 9 | 10 | console.log("START"); 11 | const result = await callWorker("./fib_worker.js", 40); 12 | console.log("AWAITED", result); 13 | console.log("END"); 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/js_versions/prisms.mjs: -------------------------------------------------------------------------------- 1 | // partialCurry! 2 | 3 | function curry(fn) { 4 | return fn.length === 0 5 | ? fn() 6 | : (...x) => curry(fn.bind(null, ...x)); 7 | } 8 | 9 | const getFieldP = curry((attr, obj) => 10 | obj && attr in obj ? obj[attr] : undefined 11 | ); 12 | 13 | const setFieldP = curry((attr, value, obj) => 14 | obj && attr in obj 15 | ? { ...obj, [attr]: value } 16 | : { ...obj } 17 | ); 18 | 19 | export {}; 20 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/sum.ts: -------------------------------------------------------------------------------- 1 | const myArray = [22, 9, 60, 12, 4, 56]; 2 | 3 | const sum = (x: number, y: number): number => x + y; 4 | 5 | const mySum = myArray.reduce(sum, 0); // 163 6 | 7 | const mySum2 = myArray.reduce((x, y) => x + y, 0); 8 | 9 | console.log(mySum, mySum2); 10 | 11 | const sumAndLog = (x: number, y: number): number => { 12 | console.log(`${x}+${y}=${x + y}`); 13 | return x + y; 14 | }; 15 | myArray.reduce(sumAndLog, 0); 16 | 17 | export {}; 18 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_go_with_arrows.ts: -------------------------------------------------------------------------------- 1 | const addLogging = any>( 2 | fn: T 3 | ): ((...args: Parameters) => ReturnType) => { 4 | return (...args: Parameters): ReturnType => { 5 | console.log(`entering ${fn.name}(${args})`); 6 | const valueToReturn = fn(...args); 7 | console.log(`exiting ${fn.name}=>${valueToReturn}`); 8 | return valueToReturn; 9 | }; 10 | }; 11 | 12 | export { addLogging }; 13 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/once.examples.ts: -------------------------------------------------------------------------------- 1 | import { 2 | once, 3 | once2, 4 | onceAndAfter, 5 | onceAndAfter2, 6 | } from "./once"; 7 | 8 | const squeak = (x: string) => console.log(x, "squeak!!"); 9 | const creak = (x: string) => console.log(x, "creak!!"); 10 | const makeSound = onceAndAfter2(squeak, creak); 11 | makeSound("door"); // "door squeak!!" 12 | makeSound("door"); // "door creak!!" 13 | makeSound("door"); // "door creak!!" 14 | makeSound("door"); // "door creak!!" 15 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/fib_worker_test_with_promise.ts: -------------------------------------------------------------------------------- 1 | import { Worker } from "worker_threads"; 2 | 3 | const callWorker = (filename: string, value: unknown) => 4 | new Promise((resolve) => { 5 | const worker = new Worker(filename); 6 | worker.on("message", resolve); 7 | worker.postMessage(value); 8 | }); 9 | 10 | console.log("START"); 11 | const result = await callWorker("./fib_worker.js", 40); 12 | console.log("AWAITED", result); 13 | console.log("END"); 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_sorting_recursively.ts: -------------------------------------------------------------------------------- 1 | const selectionSort = (arr: number[]): number[] => { 2 | if (arr.length === 0) { 3 | return []; 4 | } else { 5 | const max = Math.max(...arr); 6 | const rest = [...arr]; 7 | rest.splice(arr.indexOf(max), 1); 8 | return [...selectionSort(rest), max]; 9 | } 10 | }; 11 | 12 | console.log(selectionSort([2, 2, 0, 9, 1, 9, 6, 0])); 13 | // [0, 0, 1, 2, 2, 6, 9, 9] 14 | 15 | export { selectionSort }; 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_not_just_date_problems.ts: -------------------------------------------------------------------------------- 1 | import { jsonCopy } from "../deepCopy"; 2 | 3 | const agent = { 4 | error: new Error("It's stirred; I ordered it shaken"), 5 | map: new Map([["James", "Bond"]]), 6 | set: new Set([0, 0, 7]), 7 | regex: /007/, 8 | useLicense() { 9 | console.log("Bang! Bang!"); 10 | }, 11 | }; 12 | 13 | console.log(jsonCopy(agent)); 14 | /* 15 | { error: {}, map: {}, set: {}, regex: {} } 16 | */ 17 | 18 | export {}; 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/container.ts: -------------------------------------------------------------------------------- 1 | class Container { 2 | protected x: A; 3 | 4 | constructor(x: A) { 5 | this.x = x; 6 | } 7 | 8 | static of(x: B): Container { 9 | return new Container(x); 10 | } 11 | 12 | map(fn: (_: A) => any) { 13 | return fn(this.x); 14 | } 15 | 16 | toString() { 17 | return `${this.constructor.name}(${this.x})`; 18 | } 19 | 20 | valueOf() { 21 | return this.x; 22 | } 23 | } 24 | 25 | export { Container }; 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/try.ts: -------------------------------------------------------------------------------- 1 | import { Either } from "./either"; 2 | 3 | class Try extends Either { 4 | // @ts-expect-error Call to super() not needed 5 | constructor(fn: () => A, msg?: string) { 6 | try { 7 | return Either.of(null, fn()) as Either; 8 | } catch (e: any) { 9 | return Either.of(msg || e.message, null) as Either< 10 | string, 11 | string 12 | >; 13 | } 14 | } 15 | } 16 | 17 | export { Try }; 18 | -------------------------------------------------------------------------------- /codeForChapters/chapter 11/simpleAjax2.mjs: -------------------------------------------------------------------------------- 1 | const simpleAjax = (function () { 2 | const hard = require("hardajaxlibrary"); 3 | 4 | const convertParamsToHardStyle = (params) => { 5 | // ... 6 | }; 7 | 8 | const makeStandardUrl = (url) => { 9 | // ... 10 | }; 11 | 12 | const getUrl = (url, params, callback) => { 13 | // ... 14 | }; 15 | 16 | const postUrl = (url, params, callback) => { 17 | // ... 18 | }; 19 | 20 | return { getUrl, postUrl }; 21 | })(); 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/not.ts: -------------------------------------------------------------------------------- 1 | /* 2 | const not = (fn) => (...args) => !fn(...args); 3 | */ 4 | 5 | const not = 6 | boolean>(fn: T) => 7 | (...args: Parameters): boolean => 8 | !fn(...args); 9 | 10 | /* 11 | const filterNot = (arr) => (fn) => arr.filter(not(fn)); 12 | */ 13 | 14 | const filterNot = 15 | boolean>(arr: A[]) => 16 | (fn: T): A[] => 17 | arr.filter(not((y) => fn(y))); 18 | 19 | export { not, filterNot }; 20 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_what_could_go_wrong.ts: -------------------------------------------------------------------------------- 1 | // This code has a bug! 2 | 3 | const quicksort = (arr: A[]): A[] => { 4 | if (arr.length < 2) { 5 | return arr; 6 | } else { 7 | const pivot = arr[0]; 8 | const smaller = arr.filter((x) => x < pivot); 9 | const greaterEqual = arr.filter((x) => x >= pivot); 10 | return [ 11 | ...quicksort(smaller), 12 | ...quicksort(greaterEqual), 13 | ]; 14 | } 15 | }; 16 | 17 | export { quicksort }; 18 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_allow_for_crashing.ts: -------------------------------------------------------------------------------- 1 | const onceIfSuccess = < 2 | FNType extends (...args: any[]) => any 3 | >( 4 | fn: FNType 5 | ) => { 6 | let done = false; 7 | 8 | return ((...args: Parameters) => { 9 | if (!done) { 10 | done = true; 11 | try { 12 | return fn(...args); 13 | } catch (e) { 14 | done = false; 15 | throw e; 16 | } 17 | } 18 | }) as FNType; 19 | }; 20 | 21 | export { onceIfSuccess }; 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/isOldEnough.test.ts: -------------------------------------------------------------------------------- 1 | import { isOldEnough3 } from "./isOldEnough"; 2 | 3 | describe("isOldEnough", function () { 4 | it("is false for people younger than 18", () => { 5 | expect(isOldEnough3(2010, 2022)).toBe(false); 6 | }); 7 | 8 | it("is true for people older than 18", () => { 9 | expect(isOldEnough3(1960, 2022)).toBe(true); 10 | }); 11 | 12 | it("is true for people exactly 18", () => { 13 | expect(isOldEnough3(2004, 2022)).toBe(true); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/map.examples.ts: -------------------------------------------------------------------------------- 1 | import { myMap } from "./map"; 2 | 3 | const myArray = [22, 9, 60, 12, 4, 56]; 4 | 5 | const dup = (x: number): number => 2 * x; 6 | console.log(myMap(myArray, dup)); 7 | console.log(myArray.map(dup)); 8 | // [44, 18, 120, 24, 8, 112] both times 9 | 10 | const addDashes = (x: number): string => `-${x}-`; 11 | const myDashes = myArray.map(addDashes); 12 | console.log(myDashes); 13 | // [ '-22-', '-9-', '-60-', '-12-', '-4-', '-56-' ] 14 | 15 | export { myMap }; 16 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/test_worker_1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fibonacci 6 | 7 | 8 | Fibonacci: 9 | 10 |
11 |
12 | 13 |
14 |
15 | Result: 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/sum3.trick.ts: -------------------------------------------------------------------------------- 1 | const sum3 = (x: number, y: number, z: number): number => 2 | x + y + z; 3 | 4 | const x = {} as number; 5 | x.valueOf = () => Math.random(); 6 | 7 | const y = 1; 8 | const z = 2; 9 | 10 | console.log(sum3(x, y, z)); // 3.2034400919849431 11 | console.log(sum3(x, y, z)); // 3.8537045249277906 12 | console.log(sum3(x, y, z)); // 3.8537045249277906 13 | console.log(sum3(x, y, z)); // 3.8537045249277906 14 | console.log(sum3(x, y, z)); // 3.8537045249277906 15 | 16 | export {}; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/answers/question_01_climbing_factorial.ts: -------------------------------------------------------------------------------- 1 | const factUp = (n: number, f = 1): number => 2 | n <= f ? f : f * factUp(n, f + 1); 3 | 4 | const factUp2 = (n: number): number => { 5 | const factAux = (f: number): number => 6 | n <= f ? f : f * factAux(f + 1); 7 | return factAux(1); 8 | }; 9 | 10 | const factUp3 = (n: number): number => { 11 | const factAux = (f = 1): number => 12 | n <= f ? f : f * factAux(f + 1); 13 | return factAux(); 14 | }; 15 | 16 | export { factUp, factUp2, factUp3 }; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_everything_has_a_limit.manual.ts: -------------------------------------------------------------------------------- 1 | import { thisManyTimes } from "./question_02_everything_has_a_limit"; 2 | 3 | const squeak = (a: string) => console.log(a, "squeak!!"); 4 | 5 | const squeakTwice = thisManyTimes(squeak, 2); 6 | 7 | squeakTwice("only twice"); // "only twice squeak!!" 8 | squeakTwice("only twice"); // "only twice squeak!!" 9 | squeakTwice("only twice"); // nothing 10 | squeakTwice("only twice"); // nothing 11 | squeakTwice("only twice"); // nothing 12 | 13 | export { thisManyTimes }; 14 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/factorial.js: -------------------------------------------------------------------------------- 1 | function fact(n) { 2 | if (n === 0) { 3 | return 1; 4 | } else { 5 | return n * fact(n - 1); 6 | } 7 | } 8 | 9 | console.log(fact(5)); // 120 10 | 11 | const fact2 = (n) => { 12 | if (n === 0) { 13 | return 1; 14 | } else { 15 | const aux = fact2(n - 1); 16 | return n * aux; 17 | } 18 | }; 19 | console.log(fact2(5)); // also 120 20 | 21 | const fact3 = (n) => (n === 0 ? 1 : n * fact3(n - 1)); 22 | 23 | console.log(fact3(5)); // again 120 24 | 25 | export { fact, fact2, fact3 }; 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/copy.ts: -------------------------------------------------------------------------------- 1 | const objCopy = (obj: T): T => { 2 | const copy = Object.create(Object.getPrototypeOf(obj)); 3 | Object.getOwnPropertyNames(obj).forEach((prop: string) => 4 | Object.defineProperty( 5 | copy, 6 | prop, 7 | Object.getOwnPropertyDescriptor(obj, prop) as string 8 | ) 9 | ); 10 | return copy; 11 | }; 12 | const myObj = { fk: 22, st: 12, desc: "couple" }; 13 | const myCopy = objCopy(myObj); 14 | console.log(myObj, myCopy); // {fk: 22, st: 12, desc: "couple"}, twice 15 | 16 | export {}; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_curry_with_eval_JS.js: -------------------------------------------------------------------------------- 1 | const range = (start, stop) => 2 | new Array(stop - start).fill(0).map((v, i) => start + i); 3 | 4 | const make3 = (a, b, c) => `${a}:${b}:${c}`; 5 | 6 | function curryByEval(fn) { 7 | return eval( 8 | `${range(0, fn.length) 9 | .map((i) => `x${i}`) 10 | .join("=>")} => ${fn.name}(${range(0, fn.length) 11 | .map((i) => `x${i}`) 12 | .join(",")})` 13 | ); 14 | } 15 | 16 | console.log(curryByEval(make3).toString()); 17 | 18 | export { curryByEval }; 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_mutual_problem.ts: -------------------------------------------------------------------------------- 1 | function isEven(n: number): boolean { 2 | if (n === 0) { 3 | return true; 4 | } else { 5 | return isOdd(n - 1); 6 | } 7 | } 8 | 9 | function isOdd(n: number): boolean { 10 | if (n === 1) { 11 | return true; 12 | } else { 13 | return isEven(n - 1); 14 | } 15 | } 16 | 17 | console.log("22.. isEven?", isEven(22)); 18 | console.log("9... isOdd?", isOdd(5)); 19 | console.log("63... isEven?", isEven(63)); 20 | console.log("60... isOdd?", isOdd(60)); 21 | 22 | export {}; 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/test_worker_1.ts: -------------------------------------------------------------------------------- 1 | function fib(n: number): number { 2 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 3 | } 4 | 5 | function getNumber(): number { 6 | return Number( 7 | (document.getElementById("num") as HTMLInputElement) 8 | .value 9 | ); 10 | } 11 | 12 | function showResult(result: number): void { 13 | document.getElementById("res")!.innerText = 14 | String(result); 15 | } 16 | 17 | /* eslint-disable-next-line */ 18 | function locally(): void { 19 | showResult(fib(getNumber())); 20 | } 21 | 22 | export {}; 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/methodize.ts: -------------------------------------------------------------------------------- 1 | /* 2 | function methodize(obj, fn) { 3 | obj.prototype[fn.name] = function (...args) { 4 | return fn(this, ...args); 5 | }; 6 | } 7 | */ 8 | 9 | function methodize< 10 | T extends any[], 11 | O extends { prototype: { [key: string]: any } }, 12 | F extends (arg0: any, ...args: T) => any 13 | >(obj: O, fn: F) { 14 | obj.prototype[fn.name] = function ( 15 | this: Parameters[0], 16 | ...args: T 17 | ): ReturnType { 18 | return fn(this, ...args); 19 | }; 20 | } 21 | 22 | export { methodize }; 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/deepCopy.ts: -------------------------------------------------------------------------------- 1 | import type { OBJ } from "../common"; 2 | 3 | const jsonCopy = (obj: O): O => 4 | JSON.parse(JSON.stringify(obj)); 5 | 6 | const deepCopy = (obj: O): O => { 7 | let aux: O = obj; 8 | if (obj && typeof obj === "object") { 9 | aux = new (obj as any).constructor(); // TS hack! 10 | 11 | Object.getOwnPropertyNames(obj).forEach((prop) => { 12 | aux[prop as keyof O] = deepCopy(obj[prop]); 13 | }); 14 | } 15 | 16 | return aux; 17 | }; 18 | 19 | export { deepCopy, jsonCopy }; 20 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/test_worker_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Fibonacci 6 | 7 | 8 | Fibonacci: 9 | 10 |
11 |
12 | 13 | 14 |
15 |
16 | Result: 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/quicksort.ts: -------------------------------------------------------------------------------- 1 | const quicksort =
(arr: A[]): A[] => { 2 | if (arr.length < 2) { 3 | return arr; 4 | } else { 5 | const pivot = arr[0]; 6 | const smaller = arr.slice(1).filter((x) => x < pivot); 7 | const greaterEqual = arr 8 | .slice(1) 9 | .filter((x) => x >= pivot); 10 | return [ 11 | ...quicksort(smaller), 12 | pivot, 13 | ...quicksort(greaterEqual), 14 | ]; 15 | } 16 | }; 17 | 18 | console.log(quicksort([22, 9, 60, 12, 4, 56])); 19 | // [4, 9, 12, 22, 56, 60] 20 | 21 | export {}; 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/onceAndAfter.test.ts: -------------------------------------------------------------------------------- 1 | import { onceAndAfter } from "./onceAndAfter"; 2 | 3 | describe("onceAndAfter", () => { 4 | it("calls the 1st function once & the 2nd after", () => { 5 | const func1 = jest.fn(); 6 | const func2 = jest.fn(); 7 | const testFn = jest.fn(onceAndAfter(func1, func2)); 8 | 9 | testFn(); 10 | testFn(); 11 | testFn(); 12 | testFn(); 13 | 14 | expect(testFn).toHaveBeenCalledTimes(4); 15 | expect(func1).toHaveBeenCalledTimes(1); 16 | expect(func2).toHaveBeenCalledTimes(3); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.missing_equivalents.test.ts: -------------------------------------------------------------------------------- 1 | import { fakeFilter } from "../async"; 2 | import { someAsync } from "./question_5.missing_equivalents"; 3 | 4 | describe("someAsync", () => { 5 | it("succeeds if sometimes OK", async () => { 6 | const someEven = await someAsync( 7 | [1, 2, 3, 4], 8 | fakeFilter 9 | ); 10 | expect(someEven).toBeTruthy(); 11 | }); 12 | 13 | it("fails if never OK", () => { 14 | expect( 15 | someAsync([1, 3, 5, 7, 9], fakeFilter) 16 | ).resolves.toBeFalsy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/trampoline.ts: -------------------------------------------------------------------------------- 1 | import type { FN } from "../common"; 2 | 3 | const trampoline = (fn: FN): any => { 4 | while (typeof fn === "function") { 5 | fn = fn(); 6 | } 7 | return fn; 8 | }; 9 | 10 | class Thunk { 11 | fn: FN; 12 | constructor(fn: FN) { 13 | this.fn = fn; 14 | } 15 | } 16 | 17 | const trampoline2 = (thk: Thunk) => { 18 | while ( 19 | typeof thk === "object" && 20 | thk.constructor.name === "Thunk" 21 | ) { 22 | thk = thk.fn(); 23 | } 24 | return thk; 25 | }; 26 | 27 | export { trampoline, trampoline2 }; 28 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/once.ts: -------------------------------------------------------------------------------- 1 | const once = any>( 2 | fn: FNType 3 | ) => { 4 | let done = false; 5 | 6 | return ((...args: Parameters) => { 7 | if (!done) { 8 | done = true; 9 | return fn(...args); 10 | } 11 | }) as FNType; 12 | }; 13 | 14 | /* 15 | const sum = (a: number, b: number) => 16 | console.log("SUM", a, b); 17 | 18 | const sumOnce = once(sum); 19 | 20 | console.log("A", sumOnce(3, 5)); 21 | console.log("B", sumOnce(3, 5)); 22 | console.log("C", sumOnce(3, 5)); 23 | */ 24 | 25 | export { once }; 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/ajax.module.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | 3 | let getAjax = null; 4 | 5 | if (window.XMLHttpRequest) { 6 | // modern browsers? use XMLHttpRequest 7 | getAjax = function () { 8 | return new XMLHttpRequest(); 9 | }; 10 | } else if (window.ActiveXObject) { 11 | // it's ActiveX for IE5 and IE6 12 | getAjax = function () { 13 | new ActiveXObject("Microsoft.XMLHTTP"); 14 | }; 15 | } else { 16 | getAjax = function () { 17 | throw new Error("No Ajax support!"); 18 | }; 19 | } 20 | 21 | export { getAjax }; 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_uncurrying_the_currying.mjs: -------------------------------------------------------------------------------- 1 | const range = (start, stop) => 2 | new Array(stop - start).fill(0).map((v, i) => start + i); 3 | 4 | const uncurry = (fn, len) => 5 | eval( 6 | `(${range(0, len) 7 | .map((i) => `x${i}`) 8 | .join(",")}) => ${fn.name}${range(0, len) 9 | .map((i) => `(x${i})`) 10 | .join("")}` 11 | ); 12 | 13 | const curriedMake3 = (x0) => (x1) => (x2) => 14 | ((a, b, c) => `${a}:${b}:${c}`)(x0, x1, x2); 15 | 16 | console.log(uncurry(curriedMake3, 3).toString()); 17 | 18 | export { uncurry }; 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/invert.examples.ts: -------------------------------------------------------------------------------- 1 | import { invert } from "./invert"; 2 | 3 | const spanishComparison = (a: string, b: string): number => 4 | a.localeCompare(b, "es"); 5 | 6 | const palabras = [ 7 | "ñandú", 8 | "oasis", 9 | "mano", 10 | "natural", 11 | "mítico", 12 | "musical", 13 | ]; 14 | palabras.sort(spanishComparison); 15 | console.log(palabras); 16 | // ["mano", "mítico", "musical", "natural", "ñandú", "oasis"] 17 | 18 | palabras.sort(invert(spanishComparison)); 19 | console.log(palabras); 20 | // ["oasis", "ñandú", "natural", "musical", "mítico", "mano"] 21 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/maxStrings.ts: -------------------------------------------------------------------------------- 1 | const maxStrings = (a: string[]) => a.sort().pop(); 2 | 3 | let countries = [ 4 | "Argentina", 5 | "Uruguay", 6 | "Brasil", 7 | "Paraguay", 8 | ]; 9 | 10 | console.log(maxStrings(countries)); // "Uruguay" 11 | console.log(countries); // ["Argentina", "Brasil", "Paraguay"] 12 | 13 | const maxStrings2 = (a: string[]) => [...a].sort().pop(); 14 | 15 | countries = ["Argentina", "Uruguay", "Brasil", "Paraguay"]; 16 | 17 | console.log(maxStrings2(countries)); // "Uruguay" 18 | console.log(countries); // ["Argentina", "Uruguay", "Brasil", "Paraguay"] 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/js_versions/partialCurry.js: -------------------------------------------------------------------------------- 1 | function partialCurryByBind(fn) { 2 | return fn.length === 0 3 | ? fn() 4 | : (...x) => partialCurryByBind(fn.bind(null, ...x)); 5 | } 6 | 7 | const partialCurryByClosure = (fn) => { 8 | const curryize = 9 | (...args1) => 10 | (...args2) => { 11 | const allParams = [...args1, ...args2]; 12 | return allParams.length < fn.length 13 | ? curryize(...allParams) 14 | : fn(...allParams); 15 | }; 16 | 17 | return curryize(); 18 | }; 19 | 20 | export { partialCurryByBind, partialCurryByClosure }; 21 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/factorial.ts: -------------------------------------------------------------------------------- 1 | function fact(n: number): number { 2 | if (n === 0) { 3 | return 1; 4 | } else { 5 | return n * fact(n - 1); 6 | } 7 | } 8 | 9 | console.log(fact(5)); // 120 10 | 11 | function fact2(n: number): number { 12 | if (n === 0) { 13 | return 1; 14 | } else { 15 | const aux = fact2(n - 1); 16 | return n * aux; 17 | } 18 | } 19 | console.log(fact2(5)); // also 120 20 | 21 | const fact3 = (n: number): number => 22 | n === 0 ? 1 : n * fact3(n - 1); 23 | 24 | console.log(fact3(5)); // again 120 25 | 26 | export { fact, fact2, fact3 }; 27 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/pipeline.proposal.ts: -------------------------------------------------------------------------------- 1 | function double(x: number) { 2 | return 2 * x; 3 | } 4 | 5 | function add1(y: number) { 6 | return y + 1; 7 | } 8 | 9 | function stringify(z: number) { 10 | return `${z}${z}${z}`; 11 | } 12 | 13 | /* 14 | Do not try to run this code! 15 | It isn't accepted yet, so it will 16 | just produce syntax errors 17 | */ 18 | 19 | console.log(4 |> double |> add1 |> stringify) 20 | 21 | export {}; 22 | 23 | // npm install --save-dev @babel/plugin-proposal-pipeline-operator 24 | // https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/getByPath.ts: -------------------------------------------------------------------------------- 1 | import { deepCopy } from "./deepCopy"; 2 | import { deepFreeze } from "./deepFreeze"; 3 | 4 | import type { OBJ } from "../common"; 5 | 6 | const getField = 7 | (f: keyof O) => 8 | (obj: O) => 9 | obj[f]; 10 | 11 | const getByPath = ( 12 | arr: string[], 13 | obj: O 14 | ): any => { 15 | if (arr[0] in obj) { 16 | return arr.length > 1 17 | ? getByPath(arr.slice(1), obj[arr[0]]) 18 | : deepCopy(obj[arr[0]]); 19 | } else { 20 | return undefined; 21 | } 22 | }; 23 | 24 | export { getByPath }; 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_allow_for_crashing.manual.ts: -------------------------------------------------------------------------------- 1 | import { onceIfSuccess } from "./question_02_allow_for_crashing"; 2 | 3 | let count = 0; 4 | 5 | const crashTwice = () => { 6 | count++; 7 | if (count <= 2) { 8 | // console.log("CRASH!"); 9 | throw new Error("Crashing..."); 10 | } else { 11 | // console.log("OK NOW"); 12 | } 13 | }; 14 | 15 | const doIt = onceIfSuccess(crashTwice); 16 | 17 | expect(doIt).toThrow(); // Crash! 18 | expect(doIt).toThrow(); // Crash! 19 | doIt(); // OK NOW 20 | doIt(); // nothing 21 | doIt(); // nothing 22 | doIt(); // nothing 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/demethodize.ts: -------------------------------------------------------------------------------- 1 | const demethodize1 = 2 | (fn: (..._args: any[]) => any) => 3 | (arg0: any, ..._args: any[]) => 4 | fn.apply(arg0, _args); 5 | 6 | const demethodize2 = 7 | (fn: (..._args: any[]) => any) => 8 | (arg0: any, ..._args: any[]) => 9 | fn.call(arg0, ..._args); 10 | 11 | const demethodize3 = 12 | (fn: (..._args: any[]) => any) => 13 | (arg0: any, ..._args: any[]) => 14 | fn.bind(arg0, ..._args)(); 15 | 16 | const demethodize = demethodize1; 17 | export { 18 | demethodize, 19 | demethodize1, 20 | demethodize2, 21 | demethodize3, 22 | }; 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/partialCurry.examples.ts: -------------------------------------------------------------------------------- 1 | import { partialCurryByBind } from "./partialCurry"; 2 | 3 | const make3 = (a: string, b: number, c: string): string => 4 | `${a}:${b}:${c}`; 5 | 6 | const h1 = partialCurryByBind(make3); 7 | const h2 = h1("A"); 8 | const h3 = h2(2, "Z"); 9 | console.log(h3); // A:2:Z 10 | 11 | const h5 = h1("BE", 4); 12 | const h6 = h5("YOU"); 13 | console.log(h6); // BE:4:YOU 14 | 15 | const h7 = h5()()()("ME"); 16 | console.log(h7); // B:4:ME 17 | 18 | const h8 = partialCurryByBind(make3)("I", 8); 19 | const h9 = h8("SOME"); 20 | console.log(h9); // "I:8:SOME" 21 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/answers/question_04_tuples_to_go.ts: -------------------------------------------------------------------------------- 1 | type AccumRoundedType = [number, number]; 2 | 3 | const roundFix2a = ( 4 | accum: number, 5 | n: number 6 | ): AccumRoundedType => { 7 | const nRounded = accum > 0 ? Math.ceil(n) : Math.floor(n); 8 | accum += n - nRounded; 9 | return [accum, nRounded]; 10 | }; 11 | 12 | const roundFix2b = ([ 13 | accum, 14 | n, 15 | ]: AccumRoundedType): AccumRoundedType => { 16 | const nRounded = accum > 0 ? Math.ceil(n) : Math.floor(n); 17 | accum += n - nRounded; 18 | return [accum, nRounded]; 19 | }; 20 | 21 | export { roundFix2a, roundFix2b }; 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/arity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | const unary = fn => (...args) => fn(args[0]); 3 | */ 4 | 5 | const unary = 6 | any>( 7 | fn: T 8 | ): ((arg: Parameters[0]) => ReturnType) => 9 | (x) => 10 | fn(x); 11 | 12 | /* 13 | const arity = (n, fn) => ()...a) => fn(...a.slice(0, n)); 14 | */ 15 | 16 | function arity any>( 17 | n: number, 18 | fn: T 19 | ): (...x: Parameters) => ReturnType { 20 | return (...x: Parameters) => 21 | fn(...x.map((v, i) => (i < n ? v : undefined))); 22 | } 23 | 24 | export { unary, arity }; 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_just_say_no.ts: -------------------------------------------------------------------------------- 1 | const opposite = 2 | number | boolean>( 3 | fn: T 4 | ) => 5 | (...args: Parameters): ReturnType => { 6 | const result = fn(...args); 7 | return ( 8 | typeof result === "boolean" ? !result : -result 9 | ) as any; 10 | }; 11 | 12 | const isBig = opposite( 13 | (n: number) => (n > 1000) as boolean 14 | ); 15 | const getProduct = opposite( 16 | (n: number, p: number): number => n * p 17 | ); 18 | 19 | console.log(isBig(2000)); 20 | console.log(getProduct(22, 9)); 21 | 22 | export { opposite }; 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/answers/question_04_go_for_a_closure.ts: -------------------------------------------------------------------------------- 1 | const fibC = (() => { 2 | const cache: number[] = []; 3 | 4 | const fib2 = (n: number): number => { 5 | if (cache[n] === undefined) { 6 | if (n === 0) { 7 | cache[0] = 0; 8 | } else if (n === 1) { 9 | cache[1] = 1; 10 | } else { 11 | cache[n] = fib2(n - 2) + fib2(n - 1); 12 | } 13 | } 14 | 15 | return cache[n]; 16 | }; 17 | 18 | return fib2; 19 | })(); 20 | 21 | console.log(fibC(10)); 22 | console.log(fibC(15)); 23 | console.log(fibC(5)); 24 | console.log(fibC(8)); 25 | 26 | export { fibC }; 27 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/range.examples.ts: -------------------------------------------------------------------------------- 1 | import { range } from "./range"; 2 | 3 | console.log(range(2, 7)); // [2, 3, 4, 5, 6]; 4 | 5 | const factorialByRange = (n: number): number => 6 | range(1, n + 1).reduce((x, y) => x * y, 1); 7 | 8 | console.log(factorialByRange(5)); // 120 9 | console.log(factorialByRange(1)); // 1 10 | console.log(factorialByRange(0)); // 1 11 | 12 | const ALPHABET = range( 13 | "A".charCodeAt(0), 14 | "Z".charCodeAt(0) + 1 15 | ).map((x) => String.fromCharCode(x)); 16 | console.log(ALPHABET); 17 | // ["A", "B", "C", ... "X", "Y", "Z"] 18 | 19 | export { factorialByRange, ALPHABET, range }; 20 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/once.test.ts: -------------------------------------------------------------------------------- 1 | import { once } from "./once"; 2 | 3 | describe("once", () => { 4 | it("without 'once', a function always runs", () => { 5 | const myFn = jest.fn(); 6 | 7 | myFn(); 8 | myFn(); 9 | myFn(); 10 | 11 | expect(myFn).toHaveBeenCalledTimes(3); 12 | }); 13 | 14 | it("with 'once', a function runs one time", () => { 15 | const myFn = jest.fn(); 16 | const onceFn = jest.fn(once(myFn)); 17 | 18 | onceFn(); 19 | onceFn(); 20 | onceFn(); 21 | 22 | expect(onceFn).toHaveBeenCalledTimes(3); 23 | expect(myFn).toHaveBeenCalledTimes(1); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_the_mote.ts: -------------------------------------------------------------------------------- 1 | import { deepCopy } from "../deepCopy"; 2 | 3 | const agent = { 4 | error: new Error("It's stirred; I ordered it shaken"), 5 | map: new Map([["James", "Bond"]]), 6 | set: new Set([0, 0, 7]), 7 | regex: /007/, 8 | useLicense() { 9 | console.log("Bang! Bang!"); 10 | }, 11 | }; 12 | 13 | console.log(deepCopy(agent)); 14 | 15 | /* 16 | { 17 | error: Error: It's stirred; I ordered it shaken 18 | ...many lines snipped out 19 | map: Map(0) {}, 20 | set: Set(0) {}, 21 | regex: /(?:)/, 22 | useLicense: [Function: useLicense] 23 | } 24 | */ 25 | 26 | export {}; 27 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_everything_has_a_limit.test.ts: -------------------------------------------------------------------------------- 1 | import { thisManyTimes } from "./question_02_everything_has_a_limit"; 2 | 3 | describe("thisManyTimes", () => { 4 | it("calls the function 2 times, nothing after", () => { 5 | const fn = jest.fn(); 6 | const testFn = jest.fn(thisManyTimes(fn, 2)); 7 | 8 | testFn(); // works 9 | testFn(); // works 10 | testFn(); // nothing now 11 | testFn(); // nothing now 12 | testFn(); // nothing now 13 | testFn(); // nothing now 14 | 15 | expect(testFn).toHaveBeenCalledTimes(6); 16 | expect(fn).toHaveBeenCalledTimes(2); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/timing.examples.ts: -------------------------------------------------------------------------------- 1 | import { addTiming } from "./timing"; 2 | 3 | function subtract(a: number, b: number): number { 4 | b = changeSign(b); 5 | return a + b; 6 | } 7 | 8 | let changeSign = (a: number): number => -a; 9 | 10 | // @ts-expect-error We want to reassign the function 11 | subtract = addTiming(subtract); 12 | subtract(8, 3); 13 | 14 | console.log(); // to separate 15 | 16 | changeSign = addTiming(changeSign); 17 | // subtract(7, 5); 18 | 19 | /* 20 | subtract - normal exit 0.0217440128326416 ms 21 | 22 | changeSign - normal exit 0.0014679431915283203 ms 23 | subtract - normal exit 0.0415341854095459 ms 24 | */ 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/spread.js: -------------------------------------------------------------------------------- 1 | // sum3.js 2 | 3 | function sum3(a, b, c) { 4 | return a + b + c; 5 | } 6 | 7 | const x = [1, 2, 3]; 8 | const y = sum3(...x); // equivalent to sum3(1,2,3) 9 | console.log(y); // 6 10 | 11 | const f = [1, 2, 3]; 12 | const g = [4, ...f, 5]; // [4,1,2,3,5] 13 | const h = [...f, ...g]; // [1,2,3,4,1,2,3,5] 14 | 15 | const p = { some: 3, data: 5 }; 16 | const q = { more: 8, ...p }; // { more:8, some:3, data:5 } 17 | 18 | const numbers = [2, 2, 9, 6, 0, 1, 2, 4, 5, 6]; 19 | 20 | const minA = Math.min(...numbers); // 0 21 | 22 | const maxArray = (arr) => Math.max(...arr); 23 | 24 | const maxA = maxArray(numbers); // 9 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/js_versions/partial.js: -------------------------------------------------------------------------------- 1 | function partial(fn) { 2 | const partialize = 3 | (...args1) => 4 | (...args2) => { 5 | for ( 6 | let i = 0; 7 | i < args1.length && args2.length; 8 | i++ 9 | ) { 10 | if (args1[i] === undefined) { 11 | args1[i] = args2.shift(); 12 | } 13 | } 14 | const allParams = [...args1, ...args2]; 15 | return allParams.includes(undefined) || 16 | allParams.length < fn.length 17 | ? partialize(...allParams) 18 | : fn(...allParams); 19 | }; 20 | 21 | return partialize(); 22 | } 23 | 24 | export { partial }; 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/shuffle.test.ts: -------------------------------------------------------------------------------- 1 | import { shuffle } from "./shuffle"; 2 | 3 | describe("shuffleTest", function () { 4 | it("shouldn't change the array length", () => { 5 | const a = [22, 9, 60, 12, 4, 56]; 6 | shuffle(a); 7 | expect(a.length).toBe(6); 8 | }); 9 | 10 | it("shouldn't change the values", () => { 11 | const a = [22, 9, 60, 12, 4, 56]; 12 | shuffle(a); 13 | expect(a.includes(22)).toBe(true); 14 | expect(a.includes(9)).toBe(true); 15 | expect(a.includes(60)).toBe(true); 16 | expect(a.includes(12)).toBe(true); 17 | expect(a.includes(4)).toBe(true); 18 | expect(a.includes(56)).toBe(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.more_formal_testing.test.ts: -------------------------------------------------------------------------------- 1 | const myMap = (arr: T[], fn: (x: T) => R): R[] => 2 | arr.reduce( 3 | (x: R[], y: T): R[] => x.concat(fn(y)), 4 | [] as R[] 5 | ); 6 | 7 | describe("myMap", () => { 8 | const myArray = [22, 9, 60, 12, 4, 56]; 9 | 10 | it("duplicates values", () => { 11 | const dup = (x: number): number => 2 * x; 12 | expect(myArray.map(dup)).toEqual(myMap(myArray, dup)); 13 | }); 14 | 15 | it("add dashes", () => { 16 | const addDashes = (x: number): string => `-${x}-`; 17 | expect(myArray.map(addDashes)).toEqual( 18 | myMap(myArray, addDashes) 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_typed_demethodizing.ts: -------------------------------------------------------------------------------- 1 | const demethodize1 = 2 | any>(fn: T) => 3 | (_arg0: any, ..._args: Parameters) => 4 | fn.apply(_arg0, _args); 5 | 6 | const demethodize2 = 7 | any>(fn: T) => 8 | (_arg0: any, ..._args: Parameters): ReturnType => 9 | fn.call(_arg0, ..._args); 10 | 11 | const demethodize3 = 12 | any>(fn: T) => 13 | (_arg0: any, ..._args: Parameters): ReturnType => 14 | fn.bind(_arg0, ..._args)(); 15 | 16 | export { demethodize1, demethodize2, demethodize3 }; 17 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/pointfree.ts: -------------------------------------------------------------------------------- 1 | import { pipeTwo } from "./pipeline"; 2 | import { 3 | getDir, 4 | filterOdt, 5 | count, 6 | } from "./pipeline.examples"; 7 | 8 | const countOdtFiles3b = pipeTwo( 9 | pipeTwo(getDir, filterOdt), 10 | count 11 | ); 12 | 13 | const countOdtFiles4b = pipeTwo( 14 | getDir, 15 | pipeTwo(filterOdt, count) 16 | ); 17 | 18 | /* 19 | Note: the following code will fail if you 20 | don't have a /home/fkereki/Documents directory. 21 | Substitute another directory from your own machine. 22 | */ 23 | 24 | console.log( 25 | countOdtFiles3b("/home/fkereki/Documents"), 26 | countOdtFiles4b("/home/fkereki/Documents") 27 | ); 28 | 29 | export {}; 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/getByPath.examples.ts: -------------------------------------------------------------------------------- 1 | import { getByPath } from "./getByPath"; 2 | 3 | import { deepFreeze } from "./deepFreeze"; 4 | 5 | const myObj4 = deepFreeze({ 6 | d: 22, 7 | m: 9, 8 | o: { c: "MVD", i: "UY", f: { a: 56 } }, 9 | }); 10 | 11 | console.log(getByPath(["d"], myObj4)); // 22 12 | console.log(getByPath(["o"], myObj4)); // {c: "MVD", i: "UY", f: {a: 56}} 13 | console.log(getByPath(["o", "c"], myObj4)); // "MVD" 14 | console.log(getByPath(["o", "f", "a"], myObj4)); // 56 15 | 16 | const fObj = getByPath(["o", "f"], myObj4); 17 | console.log(fObj); // {a: 56} 18 | 19 | fObj.a = 9999; 20 | console.log(fObj); // {a: 9999} -- it's not frozen 21 | 22 | export { getByPath }; 23 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_many_arities.ts: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | const binary = (fn) => (...a) => fn(a[0], a[1]); 4 | const ternary = (fn) => (...a) => fn(a[0], a[1], a[2]); 5 | 6 | */ 7 | 8 | const binary = 9 | any>( 10 | fn: T 11 | ): (( 12 | arg0: Parameters[0], 13 | arg1: Parameters[1] 14 | ) => ReturnType) => 15 | (x, y) => 16 | fn(x, y); 17 | 18 | const ternary = 19 | any>( 20 | fn: T 21 | ): (( 22 | arg0: Parameters[0], 23 | arg1: Parameters[1], 24 | arg2: Parameters[2] 25 | ) => ReturnType) => 26 | (x, y, z) => 27 | fn(x, y, z); 28 | 29 | export { binary, ternary }; 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/sum3.ts: -------------------------------------------------------------------------------- 1 | const sum3 = new Function( 2 | "x", 3 | "y", 4 | "z", 5 | "const t = x+y+z; return t;" 6 | ); 7 | 8 | console.log(sum3(4, 6, 7)); // 17 9 | 10 | const f1 = (x: number, y: number, z: number): number => 11 | x + y + z; 12 | 13 | console.log(f1(4, 6, 7)); // also 17 14 | 15 | const f2 = (x: number, y: number, z: number): number => { 16 | return x + y + z; 17 | }; 18 | 19 | console.log(f2(4, 6, 7)); // again, 17 20 | 21 | const altSum3 = (x: number) => (y: number) => (z: number) => 22 | x + y + z; 23 | 24 | console.log(altSum3(1)(2)(3)); // 6 25 | 26 | const fn1 = altSum3(1); 27 | 28 | const fn2 = fn1(2); 29 | 30 | const fn3 = fn2(3); 31 | 32 | console.log(fn3); // also 6 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/answers/chapter_08_headline_capitalization.ts: -------------------------------------------------------------------------------- 1 | import { pipeline } from "../pipeline"; 2 | 3 | const split = (str: string) => (text: string) => 4 | text.split(str); 5 | 6 | const map = 7 | (fn: (x: string) => string) => (arr: string[]) => 8 | arr.map(fn); 9 | 10 | const firstToUpper = (word: string): string => 11 | word[0].toUpperCase() + word.substring(1).toLowerCase(); 12 | 13 | const join = (str: string) => (arr: string[]) => 14 | arr.join(str); 15 | 16 | const headline = pipeline( 17 | split(" "), 18 | map(firstToUpper), 19 | join(" ") 20 | ); 21 | 22 | console.log(headline("Alice's ADVENTURES in WoNdErLaNd")); 23 | // Alice's Adventures In Wonderland 24 | 25 | export {}; 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_wrong_function_length.ts: -------------------------------------------------------------------------------- 1 | import { range } from "../../chapter 05/range"; 2 | 3 | function arityL any>( 4 | n: number, 5 | fn: T 6 | ): (...x: Parameters) => ReturnType { 7 | const args1n = range(0, n) 8 | .map((i) => `x${i}`) 9 | .join(","); 10 | 11 | return eval(`(${args1n}) => ${fn.name}(${args1n})`); 12 | } 13 | 14 | const parseInt1 = arityL(1, parseInt); 15 | console.log(parseInt1.length); 16 | 17 | const parseInt2 = arityL(2, parseInt); 18 | console.log(parseInt2.length); 19 | 20 | /* 21 | se mantiene que TS no sabe el verdadero tipo del resultado 22 | hover sobre parseInt1, parseInt2 23 | */ 24 | 25 | export { arityL }; 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/test_worker_2.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function fib(n) { 3 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 4 | } 5 | function getNumber() { 6 | return Number(document.getElementById("num").value); 7 | } 8 | function showResult(result) { 9 | document.getElementById("res").innerText = String(result); 10 | } 11 | /* eslint-disable-next-line */ 12 | function locally() { 13 | showResult(fib(getNumber())); 14 | } 15 | var worker = new Worker( 16 | "http://localhost:8887/test_fib_worker.js" 17 | ); 18 | worker.onmessage = function (e: MessageEvent) { 19 | return showResult(e.data); 20 | }; 21 | /* eslint-disable-next-line */ 22 | function parallelly() { 23 | worker.postMessage(getNumber()); 24 | } 25 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_say_no_to_arrows.ts: -------------------------------------------------------------------------------- 1 | function once any>( 2 | fn: FNType 3 | ): FNType { 4 | let done = false; 5 | 6 | return function (...args: Parameters) { 7 | if (!done) { 8 | done = true; 9 | return fn(...args); 10 | } 11 | } as FNType; 12 | } 13 | 14 | function onceAndAfter< 15 | FNType extends (...args: any[]) => any 16 | >(f: FNType, g: FNType): FNType { 17 | let done = false; 18 | 19 | return function (...args: Parameters) { 20 | if (!done) { 21 | done = true; 22 | return f(...args); 23 | } else { 24 | return g(...args); 25 | } 26 | } as FNType; 27 | } 28 | 29 | export { once, onceAndAfter }; 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/either.ts: -------------------------------------------------------------------------------- 1 | import { Monad } from "./monad"; 2 | 3 | abstract class Either extends Monad { 4 | static of(left: C, right?: D): Left | Right { 5 | return right === undefined || right === null 6 | ? new Left(left) 7 | : new Right(right); 8 | } 9 | 10 | isLeft() { 11 | /* */ 12 | } 13 | } 14 | 15 | class Left extends Monad { 16 | isLeft() { 17 | return true; 18 | } 19 | 20 | map(_x: any) { 21 | return this; 22 | } 23 | } 24 | 25 | class Right extends Monad { 26 | isLeft() { 27 | return false; 28 | } 29 | 30 | map(fn: (_: A) => any) { 31 | return Either.of(null, fn(this.x)); 32 | } 33 | } 34 | 35 | export { Either, Left, Right }; 36 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_invert_tests.test.ts: -------------------------------------------------------------------------------- 1 | import { invert } from "../invert"; 2 | 3 | describe("invert", () => { 4 | it("can be used to sort Spanish words", () => { 5 | const spanishComparison = ( 6 | a: string, 7 | b: string 8 | ): number => a.localeCompare(b, "es"); 9 | 10 | const palabras = [ 11 | "ñandú", 12 | "oasis", 13 | "mano", 14 | "natural", 15 | "mítico", 16 | "musical", 17 | ]; 18 | 19 | expect( 20 | palabras.sort(invert(spanishComparison)) 21 | ).toEqual([ 22 | "oasis", 23 | "ñandú", 24 | "natural", 25 | "musical", 26 | "mítico", 27 | "mano", 28 | ]); 29 | }); 30 | }); 31 | 32 | export {}; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/answers/chapter_12_code_shortening.ts: -------------------------------------------------------------------------------- 1 | import type { TREE } from "../functionAsTree"; 2 | 3 | const treeSearch2 = ( 4 | findValue: A, 5 | tree: TREE 6 | ): boolean => 7 | tree( 8 | (value, left, right) => 9 | findValue === value || 10 | (findValue < value 11 | ? treeSearch2(findValue, left) 12 | : treeSearch2(findValue, right)), 13 | () => false 14 | ); 15 | 16 | const treeSearch3 = ( 17 | findValue: A, 18 | tree: TREE 19 | ): boolean => 20 | tree( 21 | (value, left, right) => 22 | findValue === value || 23 | treeSearch3( 24 | findValue, 25 | findValue < value ? left : right 26 | ), 27 | () => false 28 | ); 29 | 30 | export {}; 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/maxStrings.ts: -------------------------------------------------------------------------------- 1 | const maxStrings = (a: string[]): string | undefined => 2 | a.sort().pop(); 3 | 4 | const countries = [ 5 | "Argentina", 6 | "Uruguay", 7 | "Brasil", 8 | "Paraguay", 9 | ]; 10 | 11 | console.log(maxStrings(countries)); // "Uruguay" 12 | console.log(countries); // ["Argentina", "Brasil", "Paraguay"] 13 | 14 | const maxStrings2 = (a: string[]): string => 15 | [...a].sort().pop() as string; 16 | 17 | const maxStrings3 = (a: string[]): string => 18 | a.slice().sort().pop() as string; 19 | 20 | console.log(maxStrings2(countries)); // "Uruguay" 21 | console.log(maxStrings3(countries)); // "Uruguay" 22 | 23 | console.log(countries); 24 | // ["Argentina", "Uruguay", "Brasil", "Paraguay"] - unchanged 25 | 26 | export {}; 27 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/isOldEnough.ts: -------------------------------------------------------------------------------- 1 | const limitYear = 2004; // only good for 2022! 2 | 3 | const isOldEnough = (birthYear: number) => 4 | birthYear <= limitYear; 5 | 6 | console.log(isOldEnough(1960)); // true 7 | console.log(isOldEnough(2010)); // false 8 | 9 | const isOldEnough2 = (birthYear: number): boolean => 10 | birthYear <= new Date().getFullYear() - 18; 11 | 12 | console.log(isOldEnough2(1960)); // true 13 | console.log(isOldEnough2(2010)); // false 14 | 15 | const isOldEnough3 = ( 16 | birthYear: number, 17 | currentYear: number 18 | ): boolean => birthYear <= currentYear - 18; 19 | 20 | console.log(isOldEnough3(1960, 2022)); // true 21 | console.log(isOldEnough3(2010, 2022)); // false 22 | 23 | export { /* isOldEnough, isOldEnough2, */ isOldEnough3 }; 24 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_alternating_fns.test.ts: -------------------------------------------------------------------------------- 1 | import { alternator } from "./question_02_alternating_fns"; 2 | 3 | describe("alternator", () => { 4 | it("calls the two functions alternatively", () => { 5 | const funcA = jest.fn().mockReturnValue("A"); 6 | const funcB = jest.fn().mockReturnValue("B"); 7 | const testFn = jest.fn(alternator(funcA, funcB)); 8 | 9 | expect(testFn()).toEqual("A"); 10 | expect(testFn()).toEqual("B"); 11 | expect(testFn()).toEqual("A"); 12 | expect(testFn()).toEqual("B"); 13 | expect(testFn()).toEqual("A"); 14 | expect(testFn()).toEqual("B"); 15 | 16 | expect(testFn).toHaveBeenCalledTimes(6); 17 | expect(funcA).toHaveBeenCalledTimes(3); 18 | expect(funcB).toHaveBeenCalledTimes(3); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_shorter_typing.ts: -------------------------------------------------------------------------------- 1 | type Curry2 = P extends [infer H, ...infer T] 2 | ? (arg: H) => Curry2<[...T], R> 3 | : R; 4 | 5 | function curry( 6 | fn: (...args: A) => R 7 | ): Curry2; 8 | function curry(fn: (...args: any) => any) { 9 | return fn.length === 0 10 | ? fn() 11 | : (x: any) => curry(fn.bind(null, x)); 12 | } 13 | 14 | const make3 = (a: string, b: number, c: string): string => 15 | `${a}:${b}:${c}`; 16 | const f1 = curry(make3); 17 | // (arg: string) => (arg: number) => (arg: string) => string 18 | const f2 = f1("A"); 19 | // (arg: number) => (arg: string) => string 20 | const f3 = f2(2); 21 | // (arg: string) => string 22 | const f4 = f3("Z"); 23 | // string 24 | console.log(f4); 25 | 26 | export {}; 27 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | ], 11 | overrides: [], 12 | parser: "@typescript-eslint/parser", 13 | parserOptions: { 14 | ecmaVersion: "latest", 15 | sourceType: "module", 16 | }, 17 | plugins: ["@typescript-eslint"], 18 | rules: { 19 | "@typescript-eslint/no-explicit-any": "off", 20 | "@typescript-eslint/no-non-null-assertion": "off", 21 | /* 22 | "no-unused-vars": [ 23 | "error", 24 | { argsIgnorePattern: "^_" }, 25 | ], 26 | "@typescript-eslint/no-unused-vars": [ 27 | "error", 28 | { argsIgnorePattern: "^_" }, 29 | ], 30 | */ 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_composing_many_lenses.ts: -------------------------------------------------------------------------------- 1 | import type { OBJ } from "../../common"; 2 | import type { LENS } from "../lensesWithObjects"; 3 | import { view } from "../lensesWithObjects"; 4 | 5 | const composeTwoLenses = ( 6 | lens1: LENS, 7 | lens2: LENS 8 | ) => ({ 9 | getter: (obj: O) => lens2.getter(lens1.getter(obj)), 10 | setter: (newVal: any) => (obj: O) => 11 | lens1.setter(lens2.setter(newVal)(lens1.getter(obj)))( 12 | obj 13 | ), 14 | }); 15 | 16 | const composeManyLenses = ( 17 | ...lenses: LENS[] 18 | ) => 19 | lenses.reduce((acc, lens) => composeTwoLenses(acc, lens)); 20 | /* 21 | console.log( 22 | view(composeManyLenses(lC, lE, lG, lJ, lK), deepObject) 23 | ); 24 | /* 25 | 11, same as earlier 26 | */ 27 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.missing_equivalents.ts: -------------------------------------------------------------------------------- 1 | import { mapAsync, useResult, fakeFilter } from "../async"; 2 | 3 | const someAsync = ( 4 | arr: T[], 5 | fn: (x: T) => Promise 6 | ) => 7 | mapAsync(arr, fn).then((mapped) => mapped.some(Boolean)); 8 | 9 | (async () => { 10 | console.log("SOME 1"); 11 | const someEven = await someAsync( 12 | [1, 2, 3, 4], 13 | fakeFilter 14 | ); 15 | useResult(someEven); 16 | 17 | console.log("SOME 2"); 18 | const someEven2 = await someAsync( 19 | [1, 3, 5, 7, 9], 20 | fakeFilter 21 | ); 22 | useResult(someEven2); 23 | 24 | console.log("END"); 25 | })(); 26 | 27 | export { someAsync }; 28 | 29 | /* 30 | SOME 1 31 | 2022-10-29T14:38:00.193Z true 32 | SOME 2 33 | 2022-10-29T14:38:01.198Z false 34 | END 35 | */ 36 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/showItself.js: -------------------------------------------------------------------------------- 1 | function ShowItself1(identity) { 2 | this.identity = identity; 3 | setTimeout(function () { 4 | console.log(this.identity); 5 | }, 1000); 6 | } 7 | 8 | const x1 = ShowItself1("Functional"); 9 | 10 | function ShowItself2(identity) { 11 | this.identity = identity; 12 | const that = this; 13 | setTimeout(function () { 14 | console.log(that.identity); 15 | }, 1000); 16 | 17 | setTimeout( 18 | function () { 19 | console.log(this.identity); 20 | }.bind(this), 21 | 2000 22 | ); 23 | 24 | setTimeout(() => { 25 | console.log(this.identity); 26 | }, 3000); 27 | } 28 | 29 | const x2 = new ShowItself2("JavaScript"); 30 | // after one second, "JavaScript" 31 | // after another second, the same 32 | // after yet another second, once again 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage/** 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 29 | node_modules 30 | 31 | # vscode history 32 | .history 33 | 34 | 35 | .idea 36 | tmp 37 | 38 | # Coverage 39 | .nyc_output 40 | 41 | -------------------------------------------------------------------------------- /codeForChapters/chapter 01/answers/question_01_classes_as_1st_class.ts: -------------------------------------------------------------------------------- 1 | const makeSaluteClass = (term: string) => 2 | class { 3 | x: string; 4 | 5 | constructor(x: string) { 6 | this.x = x; 7 | } 8 | 9 | salute(y: string) { 10 | console.log(`${this.x} says "${term}" to ${y}`); 11 | } 12 | }; 13 | 14 | const Spanish = makeSaluteClass("HOLA"); 15 | new Spanish("ALFA").salute("BETA"); 16 | // ALFA says "HOLA" to BETA 17 | 18 | new (makeSaluteClass("HELLO"))("GAMMA").salute("DELTA"); 19 | // GAMMA says "HELLO" to DELTA 20 | 21 | const fullSalute = ( 22 | c: ReturnType, 23 | x: string, 24 | y: string 25 | ) => new c(x).salute(y); 26 | 27 | const French = makeSaluteClass("BON JOUR"); 28 | fullSalute(French, "EPSILON", "ZETA"); 29 | // EPSILON says "BON JOUR" to ZETA 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.range_generator.test.ts: -------------------------------------------------------------------------------- 1 | import { range4 } from "./question_5.range_generator"; 2 | 3 | describe("range4", () => { 4 | it("generates 2..5", () => { 5 | const range = range4(2, 5); 6 | expect(range.next().value).toBe(2); 7 | expect(range.next().value).toBe(3); 8 | expect(range.next().value).toBe(4); 9 | expect(range.next().value).toBe(5); 10 | expect(range.next().value).toBe(undefined); 11 | }); 12 | 13 | it("generates 5..2", () => { 14 | const range = range4(5, 2); 15 | expect([...range]).toEqual([5, 4, 3, 2]); 16 | }); 17 | 18 | it("generates 1..10 by 2", () => { 19 | const numbers = []; 20 | for (const i of range4(1, 10, 2)) { 21 | numbers.push(i); 22 | } 23 | expect(numbers).toEqual([1, 3, 5, 7, 9]); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/promisify.examples.ts: -------------------------------------------------------------------------------- 1 | import { promisify } from "./promisify"; 2 | import fs from "fs"; 3 | // or const fs = require("fs"); 4 | 5 | const cb = (err: any, data: any) => 6 | err 7 | ? console.log("ERROR", err) 8 | : console.log("SUCCESS", data); 9 | fs.readFile("./promisify.ts", cb); // success, list the data 10 | fs.readFile("./doesnt_exist.txt", cb); // failure, show exception 11 | 12 | const fspromise = promisify(fs.readFile.bind(fs)); 13 | 14 | const goodRead = (data: any) => 15 | console.log("SUCCESSFUL PROMISE", data); 16 | 17 | const badRead = (err: any) => 18 | console.log("UNSUCCESSFUL PROMISE", err); 19 | 20 | fspromise("./promisify.ts") // success 21 | .then(goodRead) 22 | .catch(badRead); 23 | 24 | fspromise("./readmenot.txt") // failure 25 | .then(goodRead) 26 | .catch(badRead); 27 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/find.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-sparse-arrays */ 2 | 3 | import type { OPT } from "../common"; 4 | 5 | const findR = ( 6 | arr: A[], 7 | cb: (x: A) => boolean 8 | ): OPT => 9 | arr.length === 0 10 | ? undefined 11 | : cb(arr[0]) 12 | ? arr[0] 13 | : findR(arr.slice(1), cb); 14 | 15 | const ccc = [1, 12, , , 5, 22, 9, 60]; 16 | 17 | const isTwentySomething = (x: number): boolean => 18 | 20 <= x && x <= 29; 19 | 20 | // @ts-expect-error It's OK: the filter won't be called with undefined 21 | console.log(findR(ccc, isTwentySomething)); // 22 22 | 23 | const isThirtySomething = (x: number): boolean => 24 | 30 <= x && x <= 39; 25 | 26 | // @ts-expect-error It's OK: the filter won't be called with undefined 27 | console.log(findR(ccc, isThirtySomething)); // undefined 28 | 29 | export {}; 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_randomizing_balancer.ts: -------------------------------------------------------------------------------- 1 | import { shuffle } from "../../chapter 04/shuffle"; 2 | 3 | const randomizer = 4 | any>(...fns: T[]) => 5 | ( 6 | ...args: Parameters 7 | ): ((...args: Parameters) => ReturnType) => { 8 | const first: T = fns.shift() as T; 9 | fns = shuffle(fns); 10 | fns.push(first); 11 | return fns[0](...args); 12 | }; 13 | 14 | const say1 = () => console.log(1); 15 | const say22 = () => console.log(22); 16 | const say333 = () => console.log(333); 17 | const say4444 = () => console.log(4444); 18 | 19 | const rrr = randomizer(say1, say22, say333, say4444); 20 | rrr(); //333 21 | rrr(); //4444 22 | rrr(); //333 23 | rrr(); //22 24 | rrr(); //333 25 | rrr(); //22 26 | rrr(); //333 27 | rrr(); //4444 28 | rrr(); //1 29 | rrr(); //4444 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/directory.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | const recursiveDir = (path: string) => { 4 | console.log(path); 5 | fs.readdirSync(path).forEach((entry) => { 6 | if (entry.startsWith(".")) { 7 | // skip it! 8 | } else { 9 | const full = path + "/" + entry; 10 | const stats = fs.lstatSync(full); 11 | if (stats.isSymbolicLink()) { 12 | console.log("L ", full); // symlink, don't follow 13 | } else if (stats.isDirectory()) { 14 | console.log("D ", full); 15 | recursiveDir(full); 16 | } else { 17 | console.log(" ", full); 18 | } 19 | } 20 | }); 21 | }; 22 | 23 | /* 24 | If you don't have a /boot directory, 25 | the following will fail; substitute 26 | another directory instead. 27 | */ 28 | recursiveDir("/boot"); 29 | 30 | export {}; 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/setByPath.examples.ts: -------------------------------------------------------------------------------- 1 | import { setByPath, updateObject } from "./setByPath"; 2 | 3 | const myObj3 = { 4 | d: 22, 5 | m: 9, 6 | o: { c: "MVD", i: "UY", f: { a: 56 } }, 7 | }; 8 | 9 | const new1 = updateObject(["m"], myObj3, "sep"); 10 | console.log(new1); 11 | // {d: 22, m: "sep", o: {c: "MVD", i: "UY", f: {a: 56}}}; 12 | 13 | const new2 = updateObject(["b"], myObj3, 220960); 14 | console.log(new2); 15 | // {d: 22, m: 9, o: {c: "MVD", i: "UY", f: {a: 56}}, b: 220960}; 16 | 17 | const new3 = updateObject(["o", "f", "a"], myObj3, 9999); 18 | console.log(new3); 19 | // {d: 22, m: 9, o: {c: "MVD", i: "UY", f: {a: 9999}}}; 20 | 21 | const new4 = updateObject( 22 | ["o", "f", "j", "k", "l"], 23 | myObj3, 24 | "deep" 25 | ); 26 | console.log(new4); 27 | // {d: 22, m: 9, o: {c: "MVD", i: "UY", f: {a: 56, j: {k: "deep"}}}}; 28 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/monad.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | /* eslint-disable no-unused-vars */ 3 | 4 | import type { FN } from "../common"; 5 | import { Functor } from "./functor"; 6 | 7 | import request from "superagent"; 8 | 9 | class Monad extends Functor { 10 | static of(x: B): Monad { 11 | return new Monad(x); 12 | } 13 | 14 | map(fn: (_: A) => B): Monad { 15 | return new Monad(fn(this.x)); 16 | } 17 | 18 | unwrap(): any { 19 | const myValue = this.x; 20 | return myValue instanceof Monad 21 | ? myValue.unwrap() 22 | : this; 23 | } 24 | 25 | chain(fn: (_: A) => B) { 26 | return this.map(fn).unwrap(); 27 | } 28 | 29 | ap(this: Monad, m: Monad) { 30 | return m.map(this.x); 31 | } 32 | } 33 | 34 | export { Monad }; 35 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.producing_a_csv.ts: -------------------------------------------------------------------------------- 1 | const myData = [ 2 | [1, 2, 3, 4], 3 | [5, 6, 7, 8], 4 | [9, 10, 11, 12], 5 | ]; 6 | 7 | const concatNumbers = (a: string, b: number): string => 8 | !a ? `${b}` : `${a},${b}`; 9 | 10 | const concatLines = (c: string, d: string): string => 11 | c + "\n" + d; 12 | 13 | const makeCSV = (data: number[][]) => 14 | data 15 | .map((x) => x.reduce(concatNumbers, "")) 16 | .reduce(concatLines, ""); 17 | console.log(makeCSV(myData)); 18 | 19 | const makeCSV2 = (data: number[][]) => 20 | data 21 | .map((x: number[]) => 22 | x.reduce( 23 | (a: string, b: number): string => 24 | !a ? `${b}` : `${a},${b}`, 25 | "" 26 | ) 27 | ) 28 | .reduce((c: string, d: string) => c + "\n" + d, ""); 29 | 30 | console.log(makeCSV2(myData)); 31 | 32 | export {}; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/test_worker_2.ts: -------------------------------------------------------------------------------- 1 | function fib(n: number): number { 2 | return n < 2 ? n : fib(n - 2) + fib(n - 1); 3 | } 4 | 5 | function getNumber(): number { 6 | return Number( 7 | (document.getElementById("num") as HTMLInputElement) 8 | .value 9 | ); 10 | } 11 | 12 | function showResult(result: number): void { 13 | document.getElementById("res")!.innerText = 14 | String(result); 15 | } 16 | 17 | /* eslint-disable-next-line */ 18 | function locally(): void { 19 | showResult(fib(getNumber())); 20 | } 21 | 22 | const worker = new Worker( 23 | "http://localhost:8887/test_fib_worker.js" 24 | ); 25 | 26 | worker.onmessage = (e: MessageEvent) => 27 | showResult(e.data); 28 | 29 | /* eslint-disable-next-line */ 30 | function parallelly(): void { 31 | worker.postMessage(getNumber()); 32 | } 33 | 34 | export {}; 35 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_freezing_by_proxying.ts: -------------------------------------------------------------------------------- 1 | import type { OBJ } from "../../common"; 2 | 3 | const proxySetAll = (obj: OBJ): OBJ => { 4 | Object.keys(obj).forEach((v) => { 5 | if (typeof obj[v] === "object") { 6 | obj[v] = proxySetAll(obj[v]); 7 | } 8 | }); 9 | 10 | return new Proxy(obj, { 11 | set() { 12 | throw new Error("DON'T MODIFY ANYTHING IN ME"); 13 | }, 14 | deleteProperty() { 15 | throw new Error("DON'T DELETE ANYTHING IN ME"); 16 | }, 17 | }) as OBJ; 18 | }; 19 | 20 | const myObj = proxySetAll({ 21 | a: 5, 22 | b: 6, 23 | c: { d: 7, e: 8 }, 24 | }); 25 | 26 | myObj.a = 777; // Uncaught Error: DON'T MODIFY ANYTHING IN ME 27 | myObj.f = 888; // Uncaught Error: DON'T MODIFY ANYTHING IN ME 28 | delete myObj.b; // Uncaught Error: DON'T DELETE ANYTHING IN ME 29 | 30 | export {}; 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/filter.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-sparse-arrays */ 2 | 3 | import type { OPT } from "../common"; 4 | 5 | const filterR = ( 6 | orig: OPT[], 7 | cb: (x: A, i: number, a: OPT[]) => boolean 8 | ): A[] => { 9 | const filterLoop = (arr: OPT[], i: number): A[] => 10 | arr.length == 0 11 | ? [] 12 | : !(0 in arr) || 13 | arr[0] === undefined || 14 | !cb(arr[0] as A, i, orig) 15 | ? filterLoop(arr.slice(1), i + 1) 16 | : ([arr[0]] as A[]).concat( 17 | filterLoop(arr.slice(1), i + 1) as A[] 18 | ); 19 | 20 | return filterLoop(orig, 0); 21 | }; 22 | 23 | const bbb = [1, 12, , , 5, 22, 9, 60]; 24 | const isOdd = (x: number): boolean => x % 2 === 1; 25 | console.log(bbb.filter((x) => x && isOdd(x))); // [1, 5, 9] 26 | console.log(filterR(bbb, isOdd)); // [1, 5, 9] 27 | 28 | export {}; 29 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/answers/chapter_08_reverse_typing.ts: -------------------------------------------------------------------------------- 1 | import { FN, pipeline } from "../pipeline"; 2 | import type { Pipeline } from "../pipeline"; 3 | 4 | type Reverse = 1 extends FNS["length"] 5 | ? [FNS[0]] 6 | : FNS extends [ 7 | infer FN1st extends FN, 8 | ...infer FNRest extends FN[] 9 | ] 10 | ? [...Reverse, FN1st] 11 | : never; 12 | 13 | type Compose = Pipeline>; 14 | 15 | function compose1( 16 | ...fns: FNS 17 | ): Compose { 18 | return pipeline(...fns.reverse()) as Compose; 19 | } 20 | 21 | const tn = (t: string): number => Number(t); 22 | const x2 = (x: number): number => x * 2; 23 | const ts = (y: number): string => `${y}`; 24 | const ds = (z: string): boolean => z > "5"; 25 | 26 | const fff = compose1(ds, ts, x2, tn); 27 | console.log(fff); 28 | 29 | export {}; 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_counting_arguments.ts: -------------------------------------------------------------------------------- 1 | type Curry

= 1 extends P["length"] 2 | ? (arg: P[0]) => R // only 1 arg 3 | : P extends [infer H, ...infer T] // 2 or more args 4 | ? (arg: H) => Curry<[...T], R> 5 | : never; 6 | 7 | function curry( 8 | fn: (...args: A) => R 9 | ): Curry; 10 | function curry(fn: (...args: any) => any) { 11 | return fn.length === 0 12 | ? fn() 13 | : (x: any) => curry(fn.bind(null, x)); 14 | } 15 | 16 | const make3 = (a: string, b: number, c: string): string => 17 | `${a}:${b}:${c}`; 18 | const f1 = curry(make3); 19 | // (arg: string) => (arg: number) => (arg: string) => string 20 | const f2 = f1("A"); 21 | // (arg: number) => (arg: string) => string 22 | const f3 = f2(2); 23 | // (arg: string) => string 24 | const f4 = f3("Z"); 25 | // string 26 | console.log(f4); 27 | 28 | export {}; 29 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/timing.ts: -------------------------------------------------------------------------------- 1 | const myGet = (): number => performance.now(); 2 | 3 | const myPut = ( 4 | text: string, 5 | name: string, 6 | tStart: number, 7 | tEnd: number 8 | ): void => 9 | console.log(`${name} - ${text} ${tEnd - tStart} ms`); 10 | 11 | function addTiming any>( 12 | fn: T, 13 | { getTime, output } = { 14 | getTime: myGet, 15 | output: myPut, 16 | } 17 | ): (...args: Parameters) => ReturnType { 18 | return (...args: Parameters): ReturnType => { 19 | const tStart = getTime(); 20 | try { 21 | const valueToReturn = fn(...args); 22 | output("normal exit", fn.name, tStart, getTime()); 23 | return valueToReturn; 24 | } catch (thrownError) { 25 | output("exception!!", fn.name, tStart, getTime()); 26 | throw thrownError; 27 | } 28 | }; 29 | } 30 | 31 | export { addTiming }; 32 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_completing_callbacks.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-sparse-arrays */ 2 | 3 | type Opt = X | undefined; 4 | 5 | const findR = ( 6 | orig: Opt[], 7 | cb: (x: A, i: number, a: Opt[]) => boolean 8 | ): Opt => { 9 | const findLoop = (arr: Opt[], i: number): Opt => 10 | arr.length === 0 11 | ? undefined 12 | : !(0 in arr) || arr[0] === undefined 13 | ? findLoop(arr.slice(1), i + 1) 14 | : cb(arr[0], i, orig) 15 | ? arr[0] 16 | : findLoop(arr.slice(1), i + 1); 17 | 18 | return findLoop(orig, 0); 19 | }; 20 | 21 | const aaa = [1, 12, , , 5, 22, 9, 60]; 22 | const bbb = [1, 12, , , 5, 32, 9, 60]; 23 | 24 | const isTwentySomething = (x: number): boolean => 25 | 20 <= x && x <= 29; 26 | 27 | console.log(findR(aaa, isTwentySomething)); // 22 28 | console.log(findR(bbb, isTwentySomething)); // undefined 29 | 30 | export {}; 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_throttling_promises.ts: -------------------------------------------------------------------------------- 1 | const promiseThrottle = < 2 | A, 3 | T extends (...x: any[]) => Promise 4 | >( 5 | fn: T, 6 | delay = 300_000 /* 5 minutes */ 7 | ): ((...x: Parameters) => Promise) => { 8 | const cache = {} as Record>; 9 | const timers = {} as Record< 10 | string, 11 | ReturnType 12 | >; 13 | return (...args) => { 14 | const strX = JSON.stringify(args); 15 | if (!(strX in timers)) { 16 | timers[strX] = setTimeout(() => { 17 | delete cache[strX]; 18 | delete timers[strX]; 19 | }, delay); 20 | } 21 | return strX in cache 22 | ? cache[strX] 23 | : (cache[strX] = fn(...args).catch((x) => { 24 | delete cache[strX]; 25 | delete timers[strX]; 26 | return x; 27 | })); 28 | }; 29 | }; 30 | 31 | export { promiseThrottle }; 32 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/curry.ts: -------------------------------------------------------------------------------- 1 | type Curry = P extends [] 2 | ? R 3 | : P extends [infer H] 4 | ? (arg: H) => R // only 1 arg 5 | : P extends [infer H, ...infer T] // 2 or more args 6 | ? (arg: H) => Curry<[...T], R> 7 | : never; 8 | 9 | function curry

( 10 | fn: (...args: P) => R 11 | ): Curry; 12 | 13 | function curry(fn: (...args: any) => any) { 14 | return fn.length === 0 15 | ? fn() 16 | : (x: any) => curry(fn.bind(null, x)); 17 | } 18 | 19 | const make3 = (a: string, b: number, c: string): string => 20 | `${a}:${b}:${c}`; 21 | const f1 = curry(make3); 22 | // (arg: string) => (arg: number) => (arg: string) => string 23 | const f2 = f1("A"); 24 | // (arg: number) => (arg: string) => string 25 | const f3 = f2(2); 26 | // (arg: string) => string 27 | const f4 = f3("Z"); 28 | // string 29 | console.log(f4); 30 | 31 | export { curry }; 32 | export type { Curry }; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/hanoi.ts: -------------------------------------------------------------------------------- 1 | type Post = "A" | "B" | "C"; 2 | 3 | const hanoi = ( 4 | disks: number, 5 | from: Post, 6 | to: Post, 7 | extra: Post 8 | ) => { 9 | if (disks === 1) { 10 | console.log( 11 | `Move disk 1 from post ${from} to post ${to}` 12 | ); 13 | } else { 14 | hanoi(disks - 1, from, extra, to); 15 | console.log( 16 | `Move disk ${disks} from post ${from} to post ${to}` 17 | ); 18 | hanoi(disks - 1, extra, to, from); 19 | } 20 | }; 21 | 22 | const hanoi2 = ( 23 | disks: number, 24 | from: Post, 25 | to: Post, 26 | extra: Post 27 | ) => { 28 | if (disks > 0) { 29 | hanoi(disks - 1, from, extra, to); 30 | console.log( 31 | `Move disk ${disks} from post ${from} to post ${to}` 32 | ); 33 | hanoi(disks - 1, extra, to, from); 34 | } 35 | }; 36 | 37 | hanoi(4, "A", "B", "C"); 38 | hanoi2(4, "A", "B", "C"); 39 | 40 | export {}; 41 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/transducing.ts: -------------------------------------------------------------------------------- 1 | import { compose } from "./compose"; 2 | 3 | import type { FN } from "../common"; 4 | 5 | const addToArray = (a: any[], v: any): any[] => { 6 | a.push(v); 7 | return a; 8 | }; 9 | 10 | const mapTR = 11 | (fn: (x: V) => W) => 12 | (reducer: (am: A, wm: W) => A) => 13 | (accum: A, value: V): A => 14 | reducer(accum, fn(value)); 15 | 16 | const filterTR = 17 | (fn: (x: V) => boolean) => 18 | (reducer: (af: A, wf: V) => A) => 19 | (accum: A, value: V): A => 20 | fn(value) ? reducer(accum, value) : accum; 21 | 22 | const transduce = (arr: A[], fns: FN[]) => 23 | arr.reduce(compose(...fns)(addToArray), []); 24 | 25 | const transduce2 = ( 26 | arr: A[], 27 | fns: FN[], 28 | reducer: FN = addToArray, 29 | initial: any = [] 30 | ) => arr.reduce(compose(...fns)(reducer), initial); 31 | 32 | export { transduce, transduce2, filterTR, mapTR }; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 11/decorator1.html: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; import { createRoot } 2 | from "react-dom/client"; const FullNameDisplay = ({ first, 3 | last }) => { return ( 4 |

9 | ); }; const ListOfNames = ({ people, heading }) => { return 10 | ( 11 |
12 |

{heading}

13 |
    14 | {people.map((v) => ( 15 | 16 | ))} 17 |
18 |
19 | ); }; const GANG_OF_FOUR = [ { first: "Erich", last: "Gamma" 20 | }, { first: "Richard", last: "Helm" }, { first: "Ralph", 21 | last: "Johnson" }, { first: "John", last: "Vlissides" } ]; 22 | const rootElement = document.getElementById("root"); const 23 | root = createRoot(rootElement); root.render( 24 | 25 | 26 | 27 | ); 28 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_curry_with_eval.ts: -------------------------------------------------------------------------------- 1 | import { range } from "../../chapter 05/range"; 2 | 3 | import type { Curry } from "../curry.examples"; 4 | 5 | function curryByEval
( 6 | _fn: (..._args: A) => R 7 | ): Curry; 8 | function curryByEval(fn: (..._args: any) => any) { 9 | return eval(`${range(0, fn.length) 10 | .map((i) => `x${i}`) 11 | .join("=>")} => 12 | ${fn.name}(${range(0, fn.length) 13 | .map((i) => `x${i}`) 14 | .join(",")})`); 15 | } 16 | 17 | function curryByEval2( 18 | _fn: (..._args: A) => R 19 | ): Curry; 20 | function curryByEval2(fn: (..._args: any) => any) { 21 | return eval(`${range(0, fn.length) 22 | .map((i) => `x${i}`) 23 | .join("=>")} => 24 | (${fn.toString()}) 25 | (${range(0, fn.length) 26 | .map((i) => `x${i}`) 27 | .join(",")})`); 28 | } 29 | 30 | export { curryByEval, curryByEval2 }; 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/validation.ts: -------------------------------------------------------------------------------- 1 | type data = { 2 | name: string; 3 | age: number; 4 | isMarried: boolean; 5 | birth: Date; 6 | numbers: number[]; // OJO! debería ser un array... 7 | setIt(x: number): void; 8 | getAge(p: number, q: string): number; 9 | }; 10 | 11 | type VALIDIFY = { 12 | [key in keyof A]?: string; 13 | }; 14 | 15 | type rrr = VALIDIFY; 16 | /* 17 | 18 | type rrr = { 19 | name?: string | undefined; 20 | age?: string | undefined; 21 | isMarried?: string | undefined; 22 | birth?: string | undefined; 23 | } 24 | 25 | */ 26 | 27 | type CHAINIFY = { 28 | [key in keyof A]: A[key] extends (...args: any[]) => any 29 | ? void extends ReturnType 30 | ? (...args: Parameters) => CHAINIFY 31 | : (...args: Parameters) => ReturnType 32 | : A[key]; 33 | }; 34 | 35 | type sss = CHAINIFY; 36 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_longest_common_subsequence.ts: -------------------------------------------------------------------------------- 1 | const LCS = (strA: string, strB: string): number => { 2 | // memoization "by hand" 3 | const cache: { [k: string]: number } = {}; 4 | 5 | const innerLCS = (strA: string, strB: string): number => { 6 | const key = strA + "/" + strB; 7 | let ret: number; 8 | 9 | if (!(key in cache)) { 10 | if (strA.length === 0 || strB.length === 0) { 11 | ret = 0; 12 | } else if (strA[0] === strB[0]) { 13 | ret = 1 + innerLCS(strA.substr(1), strB.substr(1)); 14 | } else { 15 | ret = Math.max( 16 | innerLCS(strA, strB.substr(1)), 17 | innerLCS(strA.substr(1), strB) 18 | ); 19 | } 20 | 21 | cache[key] = ret; 22 | } 23 | 24 | return cache[key]; 25 | }; 26 | 27 | return innerLCS(strA, strB); 28 | }; 29 | 30 | console.log(LCS("INTERNATIONAL", "CONTRACTOR")); // 6, as in the text 31 | 32 | export {}; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.producing_better_output.ts: -------------------------------------------------------------------------------- 1 | import { apiAnswer } from "../flat"; 2 | 3 | const better = apiAnswer 4 | .flatMap((c) => 5 | c.states.map((s) => ({ ...s, country: c.name })) 6 | ) 7 | .flatMap((s) => 8 | s.cities.map((t) => ({ 9 | ...t, 10 | state: s.name, 11 | country: s.country, 12 | })) 13 | ) 14 | .map((t) => `${t.name}, ${t.state}, ${t.country}`); 15 | 16 | console.log(better); 17 | 18 | /* 19 | [ 20 | 'Lincoln, Buenos Aires, Argentine', 21 | 'Lincoln, England, Great Britain', 22 | 'Lincoln, California, United States of America', 23 | 'Lincoln, Rhode Island, United States of America', 24 | 'Lincolnia, Virginia, United States of America', 25 | 'Lincoln Park, Michigan, United States of America', 26 | 'Lincoln, Nebraska, United States of America', 27 | 'Lincoln Park, Illinois, United States of America', 28 | 'Lincoln Square, Illinois, United States of America' 29 | ] 30 | */ 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/reduce.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-sparse-arrays */ 2 | 3 | const reduceR = ( 4 | orig: A[], 5 | cb: (acc: B, x: A, i: number, a: A[]) => B, 6 | accum: B 7 | ) => { 8 | const reduceLoop = (arr: A[], accum: B, i: number): B => 9 | arr.length == 0 10 | ? accum 11 | : !(0 in arr) || arr[0] === undefined 12 | ? reduceLoop(arr.slice(1), accum, i + 1) 13 | : reduceLoop( 14 | arr.slice(1), 15 | cb(accum, arr[0], i, orig), 16 | i + 1 17 | ); 18 | 19 | return reduceLoop(orig, accum, 0); 20 | }; 21 | 22 | const sum = (x: number, y: number): number => x + y; 23 | 24 | const bbb = [, , , 1, 2, , 5, 7, 8, 10, 21, 40]; 25 | 26 | // @ts-expect-error It's OK: sum() will not be called with undefined 27 | console.log(bbb.reduce(sum, 0)); // 94 28 | 29 | // @ts-expect-error It's OK: sum() will not be called with undefined 30 | console.log(reduceR(bbb, sum, 0)); // 94 31 | 32 | export {}; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.ranging_far_and_wide.test.ts: -------------------------------------------------------------------------------- 1 | import { range2 } from "./question_5.ranging_far_and_wide"; 2 | 3 | describe("range2()", () => { 4 | it("works from 1 to 10", () => 5 | expect(range2(1, 10)).toEqual([ 6 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 7 | ])); 8 | 9 | it("works from 1 to 10 by 2", () => 10 | expect(range2(1, 10, 2)).toEqual([1, 3, 5, 7, 9])); 11 | 12 | it("works from 21 down to 10 by -4", () => 13 | expect(range2(21, 10, -4)).toEqual([21, 17, 13])); 14 | }); 15 | 16 | /* 17 | range2(1, 10); // [1, 2, 3, 4, 5, 6, 7, 8, 9] 18 | range2(1, 10, 2); // [1, 3, 5, 7, 9] 19 | range2(1, 10, 3); // [1, 4, 7] 20 | range2(1, 10, 6); // [1, 7] 21 | range2(1, 10, 11); // [1] 22 | range2(21, 10); // [21, 20, 19, ... 13, 12, 11] 23 | range2(21, 10, -3); // [21, 18, 15, 12] 24 | range2(21, 10, -4); // [21, 17, 13] 25 | range2(21, 10, -7); // [21, 14] 26 | range2(21, 10, -12); // [21] 27 | 28 | export { range2, range2b }; 29 | */ 30 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/calculateDebt.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | const calculateDebt = async (id) => { 4 | // access a database to get a list of invoices 5 | const listOfInvoices = 6 | await mySqlConn.query(/* SQL query to get invoices */); 7 | 8 | // call a remote service to learn what's owed for each 9 | const owedAmounts = 10 | await axios.get(/* API call to get owed amounts */); 11 | 12 | const calculatedDebt = owedAmounts.reduce( 13 | (x, y) => x + y, 14 | 0 15 | ); 16 | return calculatedDebt; 17 | }; 18 | 19 | const calculateDebt2 = async ( 20 | id, 21 | { getInvoices, getOwedAmounts } = { 22 | getInvoicesFromDb, 23 | getOwedAmountFromAPI, 24 | } 25 | ) => { 26 | const listOfInvoices = await getInvoices(id); 27 | const owedAmounts = await getOwedAmounts(listOfInvoices); 28 | const calculatedDebt = owedAmounts.reduce( 29 | (x, y) => x + y, 30 | 0 31 | ); 32 | 33 | return calculatedDebt; 34 | }; 35 | 36 | export {}; 37 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/answers/chapter_07_working_stylishly.ts: -------------------------------------------------------------------------------- 1 | type Curry = P extends [infer H] 2 | ? (arg: H) => R // only 1 arg 3 | : P extends [infer H, ...infer T] // 2 or more args 4 | ? (arg: H) => Curry<[...T], R> 5 | : never; 6 | 7 | function curry( 8 | fn: (...args: A) => R 9 | ): Curry; 10 | function curry(fn: (...args: any) => any) { 11 | return fn.length === 0 12 | ? fn() 13 | : (x: any) => curry(fn.bind(null, x)); 14 | } 15 | 16 | const applyStyle = 17 | (style: string) => 18 | (text: string): string => 19 | `<${style}>${text}`; 20 | 21 | const makeBold = applyStyle("b"); 22 | console.log(makeBold("Montevideo")); 23 | // Montevideo 24 | 25 | const applyStyle2 = (style: string, text: string): string => 26 | `<${style}>${text}`; 27 | 28 | const makeUnderline = curry(applyStyle2)("u"); 29 | console.log(makeUnderline("Uruguay")); 30 | // Uruguay 31 | 32 | export { applyStyle }; 33 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/search.ts: -------------------------------------------------------------------------------- 1 | const search = (arr: A[], key: A): boolean => { 2 | if (arr.length === 0) { 3 | return false; 4 | } else if (arr[0] === key) { 5 | return true; 6 | } else { 7 | return search(arr.slice(1), key); 8 | } 9 | }; 10 | 11 | const search2 = (arr: A[], key: A): boolean => 12 | arr.length === 0 13 | ? false 14 | : arr[0] === key || search2(arr.slice(1), key); 15 | 16 | const search3 = (arr: A[], key: A): boolean => 17 | !!arr.length && 18 | (arr[0] === key || search3(arr.slice(1), key)); 19 | 20 | const myArray = [22, 9, 60, 12, 4, 56]; 21 | 22 | console.log(search(myArray, 22)); 23 | console.log(search(myArray, 23)); 24 | console.log(search(myArray, 60)); 25 | 26 | console.log(search2(myArray, 22)); 27 | console.log(search2(myArray, 23)); 28 | console.log(search2(myArray, 60)); 29 | 30 | console.log(search3(myArray, 22)); 31 | console.log(search3(myArray, 23)); 32 | console.log(search3(myArray, 60)); 33 | 34 | export {}; 35 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_odds_evens_trampolining.ts: -------------------------------------------------------------------------------- 1 | import type { FN } from "../../common"; 2 | 3 | const trampoline = (fn: FN): any => { 4 | while (typeof fn === "function") { 5 | fn = fn(); 6 | } 7 | return fn; 8 | }; 9 | 10 | function isEven(n: number, cont: FN): () => boolean { 11 | if (n === 0) { 12 | return trampoline(() => cont(true)); 13 | } else { 14 | return trampoline(() => isOdd(n - 1, (v) => cont(v))); 15 | } 16 | } 17 | 18 | function isOdd(n: number, cont: FN): () => boolean { 19 | return trampoline(() => isEven(n, (v) => cont(!v))); 20 | } 21 | 22 | function isEvenT(n: number): boolean { 23 | return trampoline(isEven(n, (x) => x)); 24 | } 25 | 26 | function isOddT(n: number): boolean { 27 | return trampoline(isOdd(n, (x) => x)); 28 | } 29 | 30 | console.log("22.. isEven?", isEvenT(22)); 31 | console.log("9... isOdd?", isOddT(5)); 32 | console.log("63... isEven?", isEvenT(63)); 33 | console.log("60... isOdd?", isOddT(60)); 34 | 35 | export {}; 36 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/fibonacci.ts: -------------------------------------------------------------------------------- 1 | const fib = (n: number): number => { 2 | if (n == 0) { 3 | return 0; 4 | } else if (n == 1) { 5 | return 1; 6 | } else { 7 | return fib(n - 2) + fib(n - 1); 8 | } 9 | }; 10 | 11 | console.log(fib(10)); // 55, a bit slowly 12 | 13 | const cache: number[] = []; 14 | 15 | const fib2 = (n: number): number => { 16 | if (cache[n] === undefined) { 17 | if (n === 0) { 18 | cache[0] = 0; 19 | } else if (n === 1) { 20 | cache[1] = 1; 21 | } else { 22 | cache[n] = fib2(n - 2) + fib2(n - 1); 23 | } 24 | } 25 | 26 | return cache[n]; 27 | }; 28 | 29 | console.log(fib2(10)); // 55, as before, but more quickly! 30 | 31 | const fib3 = (n: number): number => 32 | n < 2 ? n : fib2(n - 2) + fib2(n - 1); 33 | 34 | console.log(fib3(10)); 35 | 36 | const fib4 = (n: number, a = 0, b = 1): number => 37 | n === 0 ? a : fib4(n - 1, b, a + b); 38 | 39 | console.log(fib4(10)); // speediest! 40 | 41 | export { fib, fib2, fib3, fib4 }; 42 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_mapping_for_memory.ts: -------------------------------------------------------------------------------- 1 | const memoize4 = any>( 2 | fn: T 3 | ): ((..._x: Parameters) => ReturnType) => { 4 | const cache = new Map() as Map>; 5 | return (...args) => { 6 | const strX = JSON.stringify(args); 7 | if (!cache.has(strX)) { 8 | cache.set(strX, fn(...args)); 9 | } 10 | return cache.get(strX) as ReturnType; 11 | }; 12 | }; 13 | 14 | function fib(n: number): number { 15 | if (n == 0) { 16 | return 0; 17 | } else if (n == 1) { 18 | return 1; 19 | } else { 20 | return fib(n - 2) + fib(n - 1); 21 | } 22 | } 23 | 24 | // @ts-expect-error We want to reassign the function 25 | fib = memoize4(fib); 26 | 27 | console.log(new Date().getTime()); 28 | console.log(new Date().getTime(), fib(50)); 29 | console.log(new Date().getTime(), fib(35)); 30 | console.log(new Date().getTime(), fib(30)); 31 | console.log(new Date().getTime(), fib(10)); 32 | 33 | export { memoize4 }; 34 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/filter.ts: -------------------------------------------------------------------------------- 1 | const serviceResult = { 2 | accountsData: [ 3 | { id: "F220960K", balance: 1024 }, 4 | { id: "S120456T", balance: 2260 }, 5 | { id: "J140793A", balance: -38 }, 6 | { id: "M120396V", balance: -114 }, 7 | { id: "A120289L", balance: 55000 }, 8 | ], 9 | }; 10 | 11 | const delinquent = serviceResult.accountsData.filter( 12 | (v) => v.balance < 0 13 | ); 14 | console.log(delinquent); 15 | // two objects, with id's J140793A and M120396V 16 | 17 | const delinquentIds = delinquent.map((v) => v.id); 18 | console.log(delinquentIds); 19 | 20 | const delinquentIds2 = serviceResult.accountsData 21 | .filter((v) => v.balance < 0) 22 | .map((v) => v.id); 23 | console.log(delinquentIds2); 24 | 25 | const myFilter = (arr: T[], fn: (x: T) => boolean) => 26 | arr.reduce( 27 | (x: T[], y: T) => (fn(y) ? x.concat(y) : x), 28 | [] 29 | ); 30 | 31 | myFilter(serviceResult.accountsData, (v) => v.balance < 0); 32 | // two objects, with id's J140793A and M120396V 33 | 34 | export {}; 35 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/roundFix.test.ts: -------------------------------------------------------------------------------- 1 | import { roundFix2 } from "./roundFix"; 2 | 3 | describe("roundFix2", function () { 4 | it("rounds 3.14159->3 if differences are 0", () => { 5 | const { accum, nRounded } = roundFix2(0.0, 3.14159); 6 | expect(accum).toBeCloseTo(0.14159); 7 | expect(nRounded).toBe(3); 8 | }); 9 | 10 | it("rounds 2.71828->3 if differences are 0.14159", () => { 11 | const { accum, nRounded } = roundFix2(0.14159, 2.71828); 12 | expect(accum).toBeCloseTo(-0.14013); 13 | expect(nRounded).toBe(3); 14 | }); 15 | 16 | it("rounds 2.71828->2 if differences are -0.14013", () => { 17 | const { accum, nRounded } = roundFix2( 18 | -0.14013, 19 | 2.71828 20 | ); 21 | expect(accum).toBeCloseTo(0.57815); 22 | expect(nRounded).toBe(2); 23 | }); 24 | 25 | it("rounds 3.14159->4 if differences are 0.57815", () => { 26 | const { accum, nRounded } = roundFix2(0.57815, 3.14159); 27 | expect(accum).toBeCloseTo(-0.28026); 28 | expect(nRounded).toBe(4); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /codeForChapters/chapter 11/findRoute.js: -------------------------------------------------------------------------------- 1 | function findRoute(byMeans, fromPoint, toPoint) { 2 | switch (byMeans) { 3 | case "foot": 4 | /* 5 | find the shortest road for a walking person 6 | */ 7 | 8 | case "bicycle": 9 | /* 10 | find a route apt for a cyclist 11 | */ 12 | 13 | case "car-fastest": 14 | /* 15 | find the fastest route for a car driver 16 | */ 17 | 18 | case "car-shortest": 19 | /* 20 | find the shortest route for a car driver 21 | 22 | */ 23 | 24 | default: 25 | /* 26 | plot a straight line, or throw an error, 27 | or whatever suits you 28 | */ 29 | } 30 | } 31 | 32 | textInput$.subscribe(async (fetchResult) => { 33 | domElem = document.getElementById("myResults"); 34 | 35 | if (fetchResult !== null) { 36 | result = await fetchResult.json(); 37 | domElem.innerHTML = result.data 38 | .map((x) => `${x.city}, ${x.region}, ${x.country}`) 39 | .join("
"); 40 | } else { 41 | domElem.innerHTML = ""; 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/logging.examples.ts: -------------------------------------------------------------------------------- 1 | import { addLogging, addLogging2 } from "./logging"; 2 | 3 | function subtract(a: number, b: number): number { 4 | if (b === 0) { 5 | throw new Error("We don't subtract zero!"); 6 | } else { 7 | b = changeSign(b); 8 | return a + b; 9 | } 10 | } 11 | 12 | let changeSign = (a: number): number => -a; 13 | 14 | // @ts-expect-error We want to reassign the function 15 | subtract = addLogging(subtract); 16 | subtract(8, 3); 17 | console.log(); // to separate 18 | 19 | changeSign = addLogging(changeSign); 20 | subtract(7, 5); 21 | /* 22 | entering subtract(8,3) 23 | exiting subtract=>5 24 | 25 | entering subtract(7,5) 26 | entering changeSign(5) 27 | exiting changeSign=>-5 28 | exiting subtract=>2 29 | */ 30 | 31 | const subtract2 = addLogging2(subtract); 32 | 33 | try { 34 | subtract2(11, 0); 35 | } catch (e) { 36 | /* nothing */ 37 | } 38 | /* 39 | entering subtract(11,0) 40 | exiting subtract=>threw Error: We don't subtract zero! 41 | */ 42 | 43 | export { addLogging, addLogging2, subtract }; 44 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/ajax.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | 3 | function getAjax() { 4 | let ajax = null; 5 | if (window.XMLHttpRequest) { 6 | // modern browser? use XMLHttpRequest 7 | ajax = new XMLHttpRequest(); 8 | } else if (window.ActiveXObject) { 9 | // otherwise, use ActiveX for IE5 and IE6 10 | ajax = new ActiveXObject("Microsoft.XMLHTTP"); 11 | } else { 12 | throw new Error("No Ajax support!"); 13 | } 14 | return ajax; 15 | } 16 | 17 | (function initializeGetAjax() { 18 | let myAjax = null; 19 | 20 | if (window.XMLHttpRequest) { 21 | // modern browsers? use XMLHttpRequest 22 | myAjax = function () { 23 | return new XMLHttpRequest(); 24 | }; 25 | } else if (window.ActiveXObject) { 26 | // it's ActiveX for IE5 and IE6 27 | myAjax = function () { 28 | new ActiveXObject("Microsoft.XMLHTTP"); 29 | }; 30 | } else { 31 | myAjax = function () { 32 | throw new Error("No Ajax support!"); 33 | }; 34 | } 35 | window.getAjax = myAjax; 36 | })(); 37 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/answers/chapter_10_going_in_circles.ts: -------------------------------------------------------------------------------- 1 | import type { OBJ } from "../../common"; 2 | 3 | const deepCopy2 = (obj: O): O => { 4 | const mapped = new Map(); 5 | 6 | const deepCopy = (obj: O): O => { 7 | let aux: O = obj; 8 | if (obj && typeof obj === "object") { 9 | if (mapped.has(obj)) { 10 | return mapped.get(obj) as O; 11 | } 12 | 13 | aux = new (obj as any).constructor(); // TS hack! 14 | mapped.set(obj, aux); 15 | 16 | Object.getOwnPropertyNames(obj).forEach((prop) => { 17 | (aux as any)[prop as keyof O] = deepCopy(obj[prop]); 18 | }); 19 | } 20 | 21 | return aux; 22 | }; 23 | 24 | return deepCopy(obj); 25 | }; 26 | 27 | const circular = { 28 | a: 1, 29 | b: { c: 3, d: { e: 5, f: null } }, 30 | }; 31 | circular.b.d.f = circular.b as any; 32 | 33 | console.log(deepCopy2(circular)); // RangeError: Maximum call stack size exceeded 34 | 35 | /* 36 | { a: 1, b: { c: 3, d: { e: 5, f: [Circular *1] } } } 37 | */ 38 | 39 | export { deepCopy2 }; 40 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/deepFreeze.examples.ts: -------------------------------------------------------------------------------- 1 | import { deepFreeze } from "./deepFreeze"; 2 | import type { OBJ } from "../common"; 3 | 4 | const myObj = { d: 22, m: 9 }; 5 | console.log(myObj); 6 | 7 | // {d: 22, m: 9} 8 | 9 | const myObjB = { d: 12, m: 4 }; 10 | // Uncaught TypeError: Assignment to constant variable. 11 | 12 | myObjB.d = 12; // but this is fine! 13 | myObjB.m = 4; 14 | console.log(myObjB); 15 | // {d: 12, m: 4} 16 | 17 | const myObj2 = { d: 22, m: 9 }; 18 | console.log(myObj2); 19 | 20 | // {d: 22, m: 9} 21 | 22 | Object.freeze(myObj2); 23 | 24 | // myObj2.d = 12; // will fail! 25 | // myObj2.m = 4; // will fail too! 26 | 27 | console.log(myObj2); 28 | // Object {d: 22, m: 9} 29 | 30 | const myObj3 = { 31 | d: 22, 32 | m: 9, 33 | o: { c: "MVD", i: "UY", f: { a: 56 } }, 34 | }; 35 | 36 | Object.freeze(myObj3); 37 | console.log(myObj3); // {d:22, m:9, o:{c:"MVD", i:"UY", f:{ a:56}}} 38 | 39 | // myObj3.d = 8888; // will fail! 40 | myObj3.o.f.a = 9999; // oops, does work!! 41 | console.log(myObj3); // {d:22, m:9, o:{c:"MVD", i:"UY", f:{ a:9999 }}} 42 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/search.ts: -------------------------------------------------------------------------------- 1 | import { markers } from "./average"; 2 | 3 | const brazilData = markers.find((v) => v.name === "BR"); 4 | // {name:"BR", lat:-15.8, lon:-47.9} 5 | console.log(brazilData); 6 | 7 | const brazilIndex = markers.findIndex( 8 | (v) => v.name === "BR" 9 | ); // 2 10 | console.log(brazilIndex); 11 | 12 | const mexicoIndex = markers.findIndex( 13 | (v) => v.name === "MX" 14 | ); // -1 15 | console.log(mexicoIndex); 16 | 17 | markers.every((v) => v.lat < 0 && v.lon < 0); // false 18 | markers.some((v) => v.lat < 0 && v.lon < 0); // true 19 | 20 | const none = (arr: T[], fn: (x: T) => boolean) => 21 | arr.every((v) => !fn(v)); 22 | 23 | console.log(none(markers, (v) => v.lat < 0 && v.lon < 0)); // false 24 | 25 | declare global { 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | interface Array { 28 | none(f: (x: T) => boolean): boolean; 29 | } 30 | } 31 | 32 | Array.prototype.none = function (fn) { 33 | return this.every((v) => !fn(v)); 34 | }; 35 | 36 | console.log(markers.none((v) => v.lat < 0 && v.lon < 0)); // false 37 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/memoize.examples.ts: -------------------------------------------------------------------------------- 1 | import { addTiming } from "./timing"; 2 | import { 3 | memoize, 4 | memoize2, 5 | memoize3, 6 | memoize4, 7 | promiseMemoize, 8 | } from "./memoize"; 9 | 10 | function fib(n: number): number { 11 | if (n == 0) { 12 | return 0; 13 | } else if (n == 1) { 14 | return 1; 15 | } else { 16 | return fib(n - 2) + fib(n - 1); 17 | } 18 | } 19 | 20 | const testFib = (n: number) => fib(n); 21 | addTiming(testFib)(45); // 15,382.255 ms 22 | addTiming(testFib)(40); // 1,600.600 ms 23 | addTiming(testFib)(35); // 146.900 ms 24 | 25 | const testMemoFib = memoize((n: number) => fib(n)); 26 | addTiming(testMemoFib)(45); // 15,537.575 ms 27 | addTiming(testMemoFib)(45); // 0.005 ms 28 | addTiming(testMemoFib)(40); // 1,368.880 ms 29 | addTiming(testMemoFib)(35); // 123.970 ms 30 | 31 | // @ts-expect-error We want to reassign the function 32 | fib = memoize(fib); 33 | addTiming(testFib)(45); // 0.080 ms 34 | addTiming(testFib)(45); // 0.080 ms 35 | addTiming(testFib)(40); // 0.025 ms 36 | addTiming(testFib)(35); // 0.009 ms 37 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/sort.ts: -------------------------------------------------------------------------------- 1 | const colors = [ 2 | "violet", 3 | "indigo", 4 | "blue", 5 | "green", 6 | "yellow", 7 | "orange", 8 | "red", 9 | ]; 10 | colors.sort(); 11 | console.log(colors); 12 | // ["blue", "green", "indigo", "orange", "red", "violet", "yellow"] 13 | 14 | const someNumbers = [3, 20, 100]; 15 | someNumbers.sort(); 16 | console.log(someNumbers); 17 | // [100, 20, 3] 18 | 19 | const palabras = [ 20 | "ñandú", 21 | "oasis", 22 | "mano", 23 | "natural", 24 | "mítico", 25 | "musical", 26 | ]; 27 | palabras.sort(); 28 | console.log(palabras); 29 | // ["mano", "musical", "mítico", "natural", "oasis", "ñandú"] -- wrong result! 30 | 31 | palabras.sort((a: string, b: string) => 32 | a.localeCompare(b, "es") 33 | ); 34 | console.log(palabras); 35 | // ["mano", "mítico", "musical", "natural", "ñandú", "oasis"] 36 | 37 | const spanishComparison = (a: string, b: string) => 38 | a.localeCompare(b, "es"); 39 | 40 | palabras.sort(spanishComparison); 41 | // sorts the palabras array according to Spanish rules: 42 | // ["mano", "mítico", "musical", "natural", "ñandú", "oasis"] 43 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/demethodize.examples.ts: -------------------------------------------------------------------------------- 1 | import { 2 | demethodize1, 3 | demethodize2, 4 | demethodize3, 5 | } from "./demethodize"; 6 | 7 | const sort = demethodize1(Array.prototype.sort); 8 | const a = ["delta", "alfa", "beta", "gamma", "epsilon"]; 9 | const b = sort(a); 10 | console.log(a, b); 11 | 12 | const name = "FUNCTIONAL"; 13 | 14 | const result = name.split("").map((x) => x.toUpperCase()); 15 | // ["F", "U", "N", "C", "T", "I", "O", "N", "A", "L"] 16 | console.log(result); 17 | 18 | const map = demethodize1(Array.prototype.map); 19 | const toUpperCase = demethodize2( 20 | String.prototype.toUpperCase 21 | ); 22 | const result2 = map(name, toUpperCase); 23 | // ["F", "U", "N", "C", "T", "I", "O", "N", "A", "L"] 24 | console.log(result2); 25 | 26 | const toLocaleString = demethodize3( 27 | Number.prototype.toLocaleString 28 | ); 29 | 30 | const numbers = [2209.6, 124.56, 1048576]; 31 | const strings = numbers.map(toLocaleString); 32 | console.log(strings); 33 | 34 | // ["2,209.6", "124.56", "1,048,576"] 35 | const strings2 = map(numbers, toLocaleString); 36 | console.log(strings2); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codeForChapters/chapter 03/answers/question_03_bindless_binding.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-this-alias */ 2 | 3 | type AnyObject = Record; 4 | 5 | Function.prototype.bind = 6 | Function.prototype.bind || 7 | function (context: AnyObject, ...args1: unknown[]) { 8 | const that = this; 9 | return function (...args2: unknown[]) { 10 | return that.apply(context, [...args1, ...args2]); 11 | }; 12 | }; 13 | 14 | Function.prototype.bind ||= function ( 15 | context: AnyObject, 16 | ...args1: unknown[] 17 | ) { 18 | return (...args2: unknown[]) => 19 | this.apply(context, [...args1, ...args2]); 20 | }; 21 | 22 | type Person = { 23 | first: string; 24 | last: string; 25 | }; 26 | 27 | function printName( 28 | this: Person, 29 | year: number, 30 | born: number 31 | ) { 32 | console.log( 33 | `${this.first} ${this.last} is ${ 34 | year - born 35 | } years old in ${year}` 36 | ); 37 | return year - born; 38 | } 39 | 40 | const person = { first: "F", last: "K" }; 41 | const call = printName.bind(person, 2022); 42 | console.log(call(1960)); 43 | 44 | export {}; 45 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/pool.ts: -------------------------------------------------------------------------------- 1 | import { Worker } from "worker_threads"; 2 | 3 | type PoolEntry = { 4 | worker: Worker; 5 | filename: string; 6 | value: any; 7 | inUse: boolean; 8 | }; 9 | 10 | const pool: PoolEntry[] = []; 11 | 12 | export const workerCall = ( 13 | filename: string, 14 | value: any 15 | ): Promise => { 16 | let available = pool 17 | .filter((v) => !v.inUse) 18 | .find((x) => x.filename === filename); 19 | 20 | if (available === undefined) { 21 | // console.log("CREATING", filename, value); 22 | 23 | available = { 24 | worker: new Worker(filename), 25 | filename, 26 | value, 27 | inUse: true, 28 | } as PoolEntry; 29 | 30 | pool.push(available); 31 | } else { 32 | // console.log("REUSING", filename, available.value); 33 | } 34 | 35 | return new Promise((resolve) => { 36 | available!.inUse = true; 37 | available!.worker.on("message", (x) => { 38 | resolve(x); 39 | available!.inUse = false; 40 | // console.log("RESOLVING", filename, value, x); 41 | }); 42 | available!.worker.postMessage(value); 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/answers/question_04_tuples_to_go.test.ts: -------------------------------------------------------------------------------- 1 | import { roundFix2a, roundFix2b } from "./question_04_tuples_to_go"; 2 | 3 | describe("roundFix2a", function () { 4 | it("rounds 3.14159->3 if differences are 0", () => { 5 | const [accum, nRounded] = roundFix2a(0.0, 3.14159); 6 | expect(accum).toBeCloseTo(0.14159); 7 | expect(nRounded).toBe(3); 8 | }); 9 | 10 | it("rounds 2.71828->3 if differences are 0.14159", () => { 11 | const [accum, nRounded] = roundFix2a(0.14159, 2.71828); 12 | expect(accum).toBeCloseTo(-0.14013); 13 | expect(nRounded).toBe(3); 14 | }); 15 | }); 16 | 17 | describe("roundFix2b", function () { 18 | it("rounds 2.71828->2 if differences are -0.14013", () => { 19 | const [accum, nRounded] = roundFix2b([ 20 | -0.14013, 2.71828, 21 | ]); 22 | expect(accum).toBeCloseTo(0.57815); 23 | expect(nRounded).toBe(2); 24 | }); 25 | 26 | it("rounds 3.14159->4 if differences are 0.57815", () => { 27 | const [accum, nRounded] = roundFix2b([ 28 | 0.57815, 3.14159, 29 | ]); 30 | expect(accum).toBeCloseTo(-0.28026); 31 | expect(nRounded).toBe(4); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/monadsMadeWithFunctions.examples.use.mjs: -------------------------------------------------------------------------------- 1 | import { Maybe } from "./monadsMadeWithFunctions.0.mjs"; 2 | 3 | const client1 = Maybe.of({ 4 | id: 1, 5 | name: "UNO", 6 | address: { city: "MVD", country: "UY" }, 7 | }); 8 | const client2 = Maybe.of({ 9 | id: 2, 10 | name: "DOS", 11 | address: { city: "BA" }, 12 | }); 13 | const client3 = Maybe.of({ 14 | id: 3, 15 | name: "TRES", 16 | address: { city: "MEX", country: "MX" }, 17 | }); 18 | const client4 = Maybe.of({ 19 | id: 4, 20 | name: "CUATRO", 21 | address: {}, 22 | }); 23 | const client5 = Maybe.of({ id: 5, name: "CINCO" }); 24 | 25 | function codeToCountry(cc) { 26 | switch (cc) { 27 | case "UY": 28 | return "Uruguay"; 29 | case "AR": 30 | return "Argentina"; 31 | case "BR": 32 | return "Brasil"; 33 | } 34 | } 35 | 36 | const attr = (field) => (object) => 37 | object ? object[field] : null; 38 | 39 | [client1, client2, client3, client4, client5].forEach( 40 | (ccc) => 41 | ccc 42 | .map(attr("address")) 43 | .map(attr("country")) 44 | .map(codeToCountry) 45 | .orElse("--") 46 | .map(console.log) 47 | ); 48 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.ranging_far_and_wide.ts: -------------------------------------------------------------------------------- 1 | const range2 = ( 2 | from: number, 3 | to: number, 4 | step = Math.sign(to - from) 5 | ): number[] => { 6 | const arr = []; 7 | do { 8 | arr.push(from); 9 | from += step; 10 | } while ( 11 | (step > 0 && to > from) || 12 | (step < 0 && to < from) 13 | ); 14 | return arr; 15 | }; 16 | 17 | const range2b = ( 18 | start: number, 19 | stop: number, 20 | step: number = Math.sign(stop - start) 21 | ): number[] => 22 | new Array( 23 | step === 0 ? 1 : Math.ceil((stop - start) / step) 24 | ) 25 | .fill(0) 26 | .map((v, i) => start + i * step); 27 | 28 | console.log(range2(1, 10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9] 29 | 30 | /* 31 | range2(1, 10); // [1, 2, 3, 4, 5, 6, 7, 8, 9] 32 | range2(1, 10, 2); // [1, 3, 5, 7, 9] 33 | range2(1, 10, 3); // [1, 4, 7] 34 | range2(1, 10, 6); // [1, 7] 35 | range2(1, 10, 11); // [1] 36 | range2(21, 10); // [21, 20, 19, ... 13, 12, 11] 37 | range2(21, 10, -3); // [21, 18, 15, 12] 38 | range2(21, 10, -4); // [21, 17, 13] 39 | range2(21, 10, -7); // [21, 14] 40 | range2(21, 10, -12); // [21] 41 | */ 42 | 43 | export { range2, range2b }; 44 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/answers/chapter_09_symmetrical_queens.ts: -------------------------------------------------------------------------------- 1 | const SIZE = 8; 2 | 3 | const places = Array(SIZE); 4 | 5 | const checkPlace = (column: number, row: number): boolean => 6 | places 7 | .slice(0, column) 8 | .every( 9 | (v, i) => 10 | v !== row && Math.abs(v - row) !== column - i 11 | ); 12 | 13 | const symmetricFinder = (column = 0): void => { 14 | if (column === SIZE) { 15 | console.log(JSON.stringify(places.map((x) => x + 1))); 16 | } else if (column <= SIZE / 2) { 17 | // first half of the board? 18 | const testRowsInColumn = (j: number): void => { 19 | if (j < SIZE) { 20 | if (checkPlace(column, j)) { 21 | places[column] = j; 22 | symmetricFinder(column + 1); 23 | } 24 | testRowsInColumn(j + 1); 25 | } 26 | }; 27 | testRowsInColumn(0); 28 | } else { 29 | // second half of the board 30 | const symmetric = SIZE - 1 - places[SIZE - 1 - column]; 31 | if (checkPlace(column, symmetric)) { 32 | places[column] = symmetric; 33 | symmetricFinder(column + 1); 34 | } 35 | } 36 | }; 37 | 38 | symmetricFinder(); 39 | 40 | export {}; 41 | -------------------------------------------------------------------------------- /codeForChapters/chapter 02/answers/question_02_allow_for_crashing.test.ts: -------------------------------------------------------------------------------- 1 | import { onceIfSuccess } from "./question_02_allow_for_crashing"; 2 | 3 | describe("onceIfSuccess", () => { 4 | it("should run once if no errors", () => { 5 | const myFn = jest.fn(); 6 | const onceFn = jest.fn(onceIfSuccess(myFn)); 7 | 8 | onceFn(); 9 | onceFn(); 10 | onceFn(); 11 | 12 | expect(onceFn).toHaveBeenCalledTimes(3); 13 | expect(myFn).toHaveBeenCalledTimes(1); 14 | }); 15 | 16 | it("should run again if an exception", () => { 17 | const myFn = jest 18 | .fn() 19 | .mockImplementationOnce(() => { 20 | throw new Error("ERROR 1"); 21 | }) 22 | .mockImplementationOnce(() => { 23 | throw new Error("ERROR 2"); 24 | }) 25 | .mockReturnValue(22); 26 | 27 | const onceFn = jest.fn(onceIfSuccess(myFn)); 28 | 29 | expect(onceFn).toThrow(); 30 | expect(onceFn).toThrow(); 31 | expect(onceFn()).toBe(22); // OK now (returns 22) 32 | onceFn(); // nothing 33 | onceFn(); // nothing 34 | onceFn(); // nothing 35 | 36 | expect(onceFn).toHaveBeenCalledTimes(6); 37 | expect(myFn).toHaveBeenCalledTimes(3); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.reverse_by_summing.ts: -------------------------------------------------------------------------------- 1 | // no overloading for arrow functions! 2 | 3 | function sum(_x: number, _y: number): number; 4 | function sum(_x: string, _y: string): string; 5 | function sum(x: any, y: any): string | number { 6 | return x + y; 7 | } 8 | 9 | const reverseString2 = (str: string): string => 10 | str.split("").reduceRight(sum, ""); 11 | 12 | console.log(reverseString2("MONTEVIDEO")); 13 | 14 | const myArray = [22, 9, 60, 12, 4, 56]; 15 | 16 | console.log(myArray.reduce(sum, 0)); // 163 17 | 18 | /* 19 | console.log(sum(false, [])); 20 | console.log(sum(23, "fk")); 21 | 22 | produces 23 | 24 | TSError: ⨯ Unable to compile TypeScript: 25 | question_5.reverse by summing.ts:18:13 - error TS2769: No overload matches this call. 26 | Overload 1 of 2, '(x: number, y: number): number', gave the following error. 27 | Argument of type 'string' is not assignable to parameter of type 'number'. 28 | Overload 2 of 2, '(x: string, y: string): string', gave the following error. 29 | Argument of type 'number' is not assignable to parameter of type 'string'. 30 | 31 | 18 console.log(sum(23, "fk")); 32 | ~~~~~~~~~~~~~ 33 | */ 34 | 35 | export {}; 36 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/chaining.examples.ts: -------------------------------------------------------------------------------- 1 | import { chainify } from "./chaining"; 2 | 3 | class City { 4 | name: string; 5 | lat: number; 6 | long: number; 7 | extra: boolean; 8 | 9 | constructor(name: string, lat: number, long: number) { 10 | this.name = name; 11 | this.lat = lat; 12 | this.long = long; 13 | this.extra = lat > 0; 14 | } 15 | 16 | getName() { 17 | return this.name; 18 | } 19 | 20 | setName(newName: string): void { 21 | this.name = newName; 22 | } 23 | 24 | setLat(newLat: number): void { 25 | this.lat = newLat; 26 | } 27 | 28 | setLong(newLong: number): void { 29 | this.long = newLong; 30 | } 31 | 32 | getCoords() { 33 | return [this.lat, this.long]; 34 | } 35 | } 36 | 37 | const myCity = new City( 38 | "Montevideo, Uruguay", 39 | -34.9011, 40 | -56.1645 41 | ); 42 | console.log(myCity.getCoords(), myCity.getName()); 43 | // [ -34.9011, -56.1645 ] 'Montevideo, Uruguay' 44 | 45 | const myCity2 = chainify(myCity); 46 | 47 | console.log( 48 | myCity2 49 | .setName("Pune, India") 50 | .setLat(18.5626) 51 | .setLong(73.8087) 52 | .getCoords(), 53 | myCity.getName() 54 | ); 55 | // [ 18.5626, 73.8087 ] 'Pune, India' 56 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/mutual.ts: -------------------------------------------------------------------------------- 1 | function isEven(n: number): boolean { 2 | if (n === 0) { 3 | return true; 4 | } else { 5 | return isOdd(n - 1); 6 | } 7 | } 8 | 9 | function isOdd(n: number): boolean { 10 | return !isEven(n); 11 | } 12 | 13 | function isEven2(n: number): boolean { 14 | if (n === 0) { 15 | return true; 16 | } else { 17 | return !isEven2(n - 1); 18 | } 19 | } 20 | 21 | function isOdd2(n: number): boolean { 22 | return !isEven2(n); 23 | } 24 | 25 | function isEven3(n: number): boolean { 26 | if (n === 0) { 27 | return true; 28 | } else { 29 | return isOdd3(n - 1); 30 | } 31 | } 32 | 33 | function isOdd3(n: number): boolean { 34 | if (n === 0) { 35 | return false; 36 | } else { 37 | return isEven3(n - 1); 38 | } 39 | } 40 | 41 | console.log("22.. isEven?", isEven(22)); 42 | console.log("9... isOdd?", isOdd(5)); 43 | console.log("60... isOdd?", isOdd(10)); 44 | 45 | console.log("22.. isEven?", isEven2(22)); 46 | console.log("9... isOdd?", isOdd2(5)); 47 | console.log("60... isOdd?", isOdd2(10)); 48 | 49 | console.log("22.. isEven?", isEven3(22)); 50 | console.log("9... isOdd?", isOdd3(5)); 51 | console.log("60... isOdd?", isOdd3(10)); 52 | 53 | export {}; 54 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.wishful_thinking.ts: -------------------------------------------------------------------------------- 1 | import { Worker } from "worker_threads"; 2 | 3 | type PoolEntry = { 4 | worker: Worker; 5 | filename: string; 6 | value: any; 7 | inUse: boolean; 8 | }; 9 | 10 | const pool: PoolEntry[] = []; 11 | 12 | export const workerCall = ( 13 | filename: string, 14 | value: any 15 | ): Promise => { 16 | let available = pool 17 | .filter((v) => !v.inUse) 18 | .find((x) => x.filename === filename); 19 | 20 | if (available === undefined) { 21 | // console.log("CREATING", filename, value); 22 | 23 | available = { 24 | worker: new Worker(filename), 25 | filename, 26 | value, 27 | inUse: true, 28 | } as PoolEntry; 29 | 30 | pool.push(available); 31 | } else { 32 | // console.log("REUSING", filename, available.value); 33 | } 34 | 35 | return new Promise((resolve, reject) => { 36 | available!.inUse = true; 37 | available!.worker.on("message", (x) => { 38 | resolve(x); 39 | available!.inUse = false; 40 | }); 41 | available!.worker.on("error", (x) => { 42 | reject(x); 43 | available!.inUse = false; 44 | }); 45 | available!.worker.postMessage(value); 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/workers/pool_test.ts: -------------------------------------------------------------------------------- 1 | import { workerCall } from "./pool"; 2 | 3 | const FIB_WORKER = "./fib_worker.js"; 4 | const RANDOM_WORKER = "./random_worker.js"; 5 | 6 | const showResult = (s: string) => (x: any) => 7 | console.log(s, x); 8 | 9 | workerCall(FIB_WORKER, 35).then(showResult("fib(35)")); 10 | workerCall(RANDOM_WORKER, 3000).then(showResult("random")); 11 | workerCall(FIB_WORKER, 20).then(showResult("fib(20)")); 12 | workerCall(FIB_WORKER, 44).then(showResult("fib(44)")); 13 | workerCall(FIB_WORKER, 10).then((x) => { 14 | console.log("fib(10)", x); 15 | workerCall(FIB_WORKER, 11).then((y) => 16 | console.log("fib(11)", y) 17 | ); 18 | }); 19 | workerCall(RANDOM_WORKER, 2000).then(showResult("random")); 20 | workerCall(RANDOM_WORKER, 1000).then(showResult("random")); 21 | 22 | /* 23 | CREATING ./fib_worker.js 35 24 | CREATING ./random_worker.js 3000 25 | CREATING ./fib_worker.js 20 26 | CREATING ./fib_worker.js 44 27 | CREATING ./fib_worker.js 10 28 | CREATING ./random_worker.js 2000 29 | CREATING ./random_worker.js 1000 30 | fib(10) 55 31 | REUSING ./test_fib_worker.js 10 32 | fib(11) 89 33 | fib(20) 6765 34 | fib(35) 9227465 35 | random 602 36 | random 135 37 | random 17 38 | fib(44) 701408733 39 | */ 40 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/methodize.examples.ts: -------------------------------------------------------------------------------- 1 | import { methodize } from "./methodize"; 2 | 3 | declare global { 4 | interface String { 5 | reverse(y?: boolean): string; 6 | } 7 | } 8 | 9 | function reverse(x: string, y = false): string { 10 | return x 11 | .split("") 12 | .reverse() 13 | .join(y ? "-" : ""); 14 | } 15 | 16 | methodize(String, reverse); 17 | 18 | /* The previous is equivalent to: 19 | String.prototype.reverse = 20 | function (this: string, y): string { 21 | return reverse(this, y); 22 | }; 23 | */ 24 | 25 | console.log("MONTEVIDEO".reverse()); 26 | console.log("MONTEVIDEO".reverse(true)); 27 | 28 | declare global { 29 | // esl int-disable-next-line @typescript-eslint/no-unused-vars 30 | interface Array { 31 | average(this: Array): number; 32 | } 33 | } 34 | 35 | function average(x: number[]): number { 36 | return ( 37 | x.reduce((x: number, y: number) => x + y, 0) / x.length 38 | ); 39 | } 40 | methodize(Array, average); 41 | 42 | const myAvg = [22, 9, 60, 12, 4, 56].average(); // 27.166667 43 | console.log(myAvg); 44 | 45 | console.log([1, 2, 3, 4].average()); // this is OK 46 | // @ts-expect-error Wrong! 47 | console.log(["a", "b"].average()); // this is not OK 48 | -------------------------------------------------------------------------------- /codeForChapters/chapter 08/pipetwo.test.ts: -------------------------------------------------------------------------------- 1 | import type { FN } from "../common"; 2 | 3 | const pipeTwo = 4 | (f: F, g: G) => 5 | (...args: Parameters): ReturnType => 6 | g(f(...args)); 7 | 8 | describe("pipeTwo", function () { 9 | it("works with single arguments", () => { 10 | const fn1 = jest.fn().mockReturnValue(1); 11 | const fn2 = jest.fn().mockReturnValue(2); 12 | 13 | const pipe = pipeTwo(fn1, fn2); 14 | const result = pipe(22); 15 | 16 | expect(fn1).toHaveBeenCalledTimes(1); 17 | expect(fn2).toHaveBeenCalledTimes(1); 18 | expect(fn1).toHaveBeenCalledWith(22); 19 | expect(fn2).toHaveBeenCalledWith(1); 20 | expect(result).toBe(2); 21 | }); 22 | 23 | it("works with multiple arguments", () => { 24 | const fn1 = jest.fn().mockReturnValue(11); 25 | const fn2 = jest.fn().mockReturnValue(22); 26 | 27 | const pipe = pipeTwo(fn1, fn2); 28 | const result = pipe(12, 4, 56); 29 | 30 | expect(fn1).toHaveBeenCalledTimes(1); 31 | expect(fn2).toHaveBeenCalledTimes(1); 32 | expect(fn1).toHaveBeenCalledWith(12, 4, 56); 33 | 34 | expect(fn2).toHaveBeenCalledWith(11); 35 | expect(result).toBe(22); 36 | }); 37 | }); 38 | 39 | export {}; 40 | -------------------------------------------------------------------------------- /codeForChapters/chapter 12/answers/chapter_12_extending_prototypes.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Boolean { 3 | map(_f: (_x: boolean) => boolean): boolean; 4 | } 5 | } 6 | 7 | Boolean.prototype.map = function ( 8 | this: boolean, 9 | fn: (_x: boolean) => any 10 | ) { 11 | return !!fn(this); 12 | }; 13 | 14 | const t = true; 15 | const f = false; 16 | const negate = (x: boolean) => !x; 17 | 18 | console.log(t.map(negate), f.map(negate)); 19 | // false true 20 | 21 | declare global { 22 | interface Number { 23 | map(_f: (_x: number) => number): number; 24 | } 25 | } 26 | 27 | Number.prototype.map = function ( 28 | this: number, 29 | fn: (_x: number) => number 30 | ) { 31 | return Number(fn(this)); 32 | }; 33 | 34 | const n = 22; 35 | const add1 = (n: number) => n + 1; 36 | console.log(n.map(add1)); 37 | 38 | declare global { 39 | interface String { 40 | map(_f: (_x: string) => string): string; 41 | } 42 | } 43 | 44 | String.prototype.map = function ( 45 | this: string, 46 | fn: (_x: string) => string 47 | ) { 48 | return String(fn(this)); 49 | }; 50 | 51 | const s = "Montevideo"; 52 | const addBangs = (s: string): string => s + "!!!"; 53 | 54 | console.log(s.map(addBangs)); 55 | 56 | export {}; 57 | -------------------------------------------------------------------------------- /codeForChapters/chapter 11/simpleAjax1.mjs: -------------------------------------------------------------------------------- 1 | // simpleAjax.js 2 | 3 | import * as hard from "hardajaxlibrary"; 4 | // import the other library that does Ajax calls 5 | // but in a hard, difficult way, requiring complex code 6 | 7 | const convertParamsToHardStyle = (params) => { 8 | // do some internal steps to convert params 9 | // into whatever the hard library may require 10 | }; 11 | 12 | const makeStandardUrl = (url) => { 13 | // make sure the url is in the standard 14 | // way for the hard library 15 | }; 16 | 17 | const getUrl = (url, params, callback) => { 18 | const xhr = hard.createAnXmlHttpRequestObject(); 19 | hard.initializeAjaxCall(xhr); 20 | const standardUrl = makeStandardUrl(url); 21 | hard.setUrl(xhr, standardUrl); 22 | const convertedParams = convertParamsToHardStyle(params); 23 | hard.setAdditionalParameters(params); 24 | hard.setCallback(callback); 25 | if (hard.everythingOk(xhr)) { 26 | hard.doAjaxCall(xhr); 27 | } else { 28 | throw new Error("ajax failure"); 29 | } 30 | }; 31 | 32 | const postUrl = (url, params, callback) => { 33 | // some similarly complex code 34 | // to do a POST using the hard library 35 | }; 36 | 37 | export { getUrl, postUrl }; // the only methods that will be seen 38 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/deepCopy.examples.ts: -------------------------------------------------------------------------------- 1 | import { deepCopy, jsonCopy } from "./deepCopy"; 2 | 3 | import type { OBJ } from "../common"; 4 | 5 | const oldObject = { 6 | d: 22, 7 | m: 9, 8 | o: { c: "MVD", i: "UY", f: { a: 56 } }, 9 | }; 10 | 11 | const newObject = { 12 | d: oldObject.d, 13 | m: oldObject.m, 14 | o: { 15 | c: oldObject.o.c, 16 | i: oldObject.o.i, 17 | f: { a: oldObject.o.f.a }, 18 | }, 19 | }; 20 | 21 | const myObj = { d: 22, m: 9 }; 22 | const newObj1 = Object.assign({}, myObj); 23 | const newObj2 = { ...myObj }; 24 | console.log(myObj, newObj1, newObj2); 25 | 26 | const myArray = [1, 2, 3, 4]; 27 | const newArray1 = myArray.slice(); 28 | const newArray2 = [...myArray]; 29 | console.log(myArray, newArray1, newArray2); 30 | 31 | const newObject2 = Object.assign({}, oldObject); 32 | 33 | newObject2.d = 8888; 34 | newObject2.o.f.a = 9999; 35 | 36 | console.log(newObject2); 37 | // {d:8888, m:9, o: {c:"MVD", i:"UY", f: {a:9999}}} -- ok 38 | 39 | console.log(oldObject); 40 | // {d:22, m:9, o: {c:"MVD", i:"UY", f: {a:9999}}} -- oops!! 41 | 42 | const myDate = new Date(); 43 | const newDate = jsonCopy(myDate); 44 | console.log(typeof myDate, typeof newDate); // object string 45 | 46 | export { deepCopy, jsonCopy }; 47 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/answers/question_06_missing_companion.ts: -------------------------------------------------------------------------------- 1 | const setField = ( 2 | attr: keyof D, 3 | value: any, 4 | obj: D 5 | ) => ({ 6 | ...obj, 7 | [attr]: value, 8 | }); 9 | 10 | const myObj = { 11 | name: { 12 | first: "Federico", 13 | last: "Kereki", 14 | }, 15 | country: "India", 16 | }; 17 | const myObj1 = setField("country", "Uruguay", myObj); 18 | console.log(myObj1); 19 | 20 | const deepCopy = (o: any) => o; // fake version! 21 | 22 | const setField2 = ( 23 | attr: keyof D, 24 | value: any, 25 | obj: D 26 | ) => ({ 27 | ...deepCopy(obj), 28 | [attr]: value, 29 | }); 30 | 31 | /* 32 | const setByPath = (arr, value, obj) => { 33 | if (!(arr[0] in obj)) { 34 | obj[arr[0]] = 35 | arr.length === 1 36 | ? null 37 | : Number.isInteger(arr[1]) 38 | ? [] 39 | : {}; 40 | } 41 | if (arr.length > 1) { 42 | return setByPath(arr.slice(1), value, obj[arr[0]]); 43 | } else { 44 | obj[arr[0]] = value; 45 | return obj; 46 | } 47 | }; 48 | 49 | const setField2 = (attr, value, obj) => 50 | setByPath([attr], value, obj); 51 | 52 | const myObj2 = setField2("country", "Uruguay", myObj); 53 | console.log(myObj2); 54 | 55 | */ 56 | export { setField, setField2 }; 57 | -------------------------------------------------------------------------------- /codeForChapters/chapter 09/pipeline.ts: -------------------------------------------------------------------------------- 1 | import type { Pipeline, FN } from "../chapter 08/pipeline"; 2 | 3 | function pipelineR( 4 | ...fns: FNS 5 | ): Pipeline; 6 | function pipelineR(...fns: FNS): FN { 7 | return fns.length === 1 8 | ? fns[0] 9 | : (...args) => 10 | pipelineR(...fns.slice(1))(fns[0](...args)); 11 | } 12 | 13 | function pipelineR2( 14 | ...fns: FNS 15 | ): Pipeline; 16 | function pipelineR2(...fns: FNS): FN { 17 | return fns.length === 0 18 | ? (...args) => args[0] 19 | : (...args) => 20 | pipelineR2(...fns.slice(1))(fns[0](...args)); 21 | } 22 | 23 | const plus1 = (x: number): number => x + 1; 24 | const by10 = (x: number): number => x * 10; 25 | 26 | console.log( 27 | pipelineR( 28 | by10, 29 | plus1, 30 | plus1, 31 | plus1, 32 | by10, 33 | plus1, 34 | by10, 35 | by10, 36 | plus1, 37 | plus1, 38 | plus1 39 | )(2) 40 | ); 41 | // 23103 42 | 43 | console.log( 44 | pipelineR2( 45 | by10, 46 | plus1, 47 | plus1, 48 | plus1, 49 | by10, 50 | plus1, 51 | by10, 52 | by10, 53 | plus1, 54 | plus1, 55 | plus1 56 | )(2) 57 | ); 58 | 59 | export {}; 60 | -------------------------------------------------------------------------------- /codeForChapters/chapter 10/setByPath.ts: -------------------------------------------------------------------------------- 1 | import { deepCopy } from "./deepCopy"; 2 | import { deepFreeze } from "./deepFreeze"; 3 | 4 | import type { OBJ } from "../common"; 5 | 6 | const setByPath = ( 7 | arr: string[], 8 | value: any, 9 | obj: O 10 | ): O => { 11 | if (!(arr[0] in obj)) { 12 | // TS objects to obj[arr[0]]=... 13 | 14 | (obj as any)[arr[0]] = 15 | arr.length === 1 16 | ? null 17 | : Number.isInteger(arr[1]) 18 | ? [] 19 | : {}; 20 | 21 | /* This also works: 22 | 23 | Reflect.set( 24 | obj, 25 | arr[0], 26 | arr.length === 1 27 | ? null 28 | : Number.isInteger(arr[1]) 29 | ? [] 30 | : {} 31 | ); 32 | */ 33 | } 34 | 35 | if (arr.length > 1) { 36 | return setByPath(arr.slice(1), value, obj[arr[0]]); 37 | } else { 38 | obj[arr[0] as keyof O] = value; 39 | return obj; 40 | } 41 | }; 42 | 43 | const updateObject = ( 44 | arr: string[], 45 | obj: O, 46 | value: any 47 | ) => { 48 | const newObj = deepCopy(obj); 49 | setByPath(arr, value, newObj); 50 | return deepFreeze(newObj); 51 | }; 52 | 53 | export { setByPath, updateObject }; 54 | 55 | // https://github.com/microsoft/TypeScript/issues/47357 56 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/logging3.test.ts: -------------------------------------------------------------------------------- 1 | import { addLogging3 } from "./logging3"; 2 | 3 | describe("addLogging3()", function () { 4 | it("should call the provided logger", () => { 5 | const logger = jest.fn(); 6 | 7 | let something = (a: number, b: number): string => 8 | `result=${a}:${b}`; 9 | 10 | something = addLogging3(something, logger); 11 | 12 | something(22, 9); 13 | 14 | expect(logger).toHaveBeenCalledTimes(2); 15 | expect(logger).toHaveBeenNthCalledWith( 16 | 1, 17 | "entering something(22,9)" 18 | ); 19 | expect(logger).toHaveBeenNthCalledWith( 20 | 2, 21 | "exiting something=>result=22:9" 22 | ); 23 | }); 24 | 25 | it("a throwing function should be reported", () => { 26 | const logger = jest.fn(); 27 | 28 | let thrower = () => { 29 | throw "CRASH!"; 30 | }; 31 | 32 | thrower = addLogging3(thrower, logger); 33 | 34 | try { 35 | thrower(); 36 | } catch (e) { 37 | expect(logger).toHaveBeenCalledTimes(2); 38 | expect(logger).toHaveBeenNthCalledWith( 39 | 1, 40 | "entering thrower()" 41 | ); 42 | expect(logger).toHaveBeenNthCalledWith( 43 | 2, 44 | "exiting thrower=>threw CRASH!" 45 | ); 46 | } 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /codeForChapters/chapter 07/partialCurry.ts: -------------------------------------------------------------------------------- 1 | import type { TypesMatch } from "./partial"; 2 | 3 | type Minus = [X, Y] extends [ 4 | [any, ...infer XT], 5 | [any, ...infer YT] 6 | ] 7 | ? Minus 8 | : X; 9 | 10 | type PartialCurry

= ( 11 | ...x: A 12 | ) => TypesMatch extends never 13 | ? never 14 | : P extends any[] 15 | ? A["length"] extends P["length"] 16 | ? R 17 | : PartialCurry, R> 18 | : never; 19 | 20 | function partialCurryByBind( 21 | fn: (...args: A) => R 22 | ): PartialCurry; 23 | function partialCurryByBind(fn: (...args: any) => any) { 24 | return fn.length === 0 25 | ? fn() 26 | : (...x: any[]) => 27 | partialCurryByBind(fn.bind(null, ...x)); 28 | } 29 | 30 | function partialCurryByClosure

( 31 | fn: (...a: P) => R 32 | ): PartialCurry; 33 | function partialCurryByClosure(fn: (...a: any) => any) { 34 | const curryize = 35 | (...args1: any[]) => 36 | (...args2: any[]) => { 37 | const allParams = [...args1, ...args2]; 38 | return allParams.length < fn.length 39 | ? curryize(...allParams) 40 | : fn(...allParams); 41 | }; 42 | 43 | return curryize(); 44 | } 45 | 46 | export { partialCurryByBind, partialCurryByClosure }; 47 | -------------------------------------------------------------------------------- /codeForChapters/chapter 06/not.examples.ts: -------------------------------------------------------------------------------- 1 | import { not, filterNot } from "./not"; 2 | 3 | type AccountData = { 4 | id: string; 5 | balance: number; 6 | }; 7 | 8 | const serviceResult = { 9 | accountsData: [ 10 | { 11 | id: "F220960K", 12 | balance: 1024, 13 | }, 14 | { 15 | id: "S120456T", 16 | balance: 2260, 17 | }, 18 | { 19 | id: "J140793A", 20 | balance: -38, 21 | }, 22 | { 23 | id: "M120396V", 24 | balance: -114, 25 | }, 26 | { 27 | id: "A120289L", 28 | balance: 55000, 29 | }, 30 | ], 31 | }; 32 | 33 | const delinquent = serviceResult.accountsData.filter( 34 | (v) => v.balance < 0 35 | ); 36 | console.log(delinquent); 37 | 38 | const notDelinquent = serviceResult.accountsData.filter( 39 | (v) => v.balance >= 0 40 | ); 41 | console.log(notDelinquent); 42 | 43 | const notDelinquent2 = serviceResult.accountsData.filter( 44 | (v) => !(v.balance < 0) 45 | ); 46 | console.log(notDelinquent2); 47 | 48 | const isNegativeBalance = (v: AccountData) => v.balance < 0; 49 | 50 | const delinquent2 = serviceResult.accountsData.filter( 51 | isNegativeBalance 52 | ); 53 | console.log(delinquent2); 54 | 55 | const notDelinquent3 = serviceResult.accountsData.filter( 56 | not(isNegativeBalance) 57 | ); 58 | console.log(notDelinquent3); 59 | -------------------------------------------------------------------------------- /codeForChapters/chapter 11/decorator2.html: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | const FullNameDisplay = ({ first, last }) => { 5 | return ( 6 |

11 | ); 12 | }; 13 | 14 | const makeVisible = (component) => { 15 | return ( 16 |
17 | {component} 18 |
19 | ); 20 | }; 21 | 22 | const ListOfNames = ({ people, heading }) => { 23 | return ( 24 |
25 |

{heading}

26 |
    27 | {people.map((v) => 28 | makeVisible( 29 | 33 | ) 34 | )} 35 |
36 |
37 | ); 38 | }; 39 | 40 | const GANG_OF_FOUR = [ 41 | { first: "Erich", last: "Gamma" }, 42 | { first: "Richard", last: "Helm" }, 43 | { first: "Ralph", last: "Johnson" }, 44 | { first: "John", last: "Vlissides" } 45 | ]; 46 | 47 | const rootElement = document.getElementById("root"); 48 | const root = createRoot(rootElement); 49 | 50 | root.render( 51 | 52 | 53 | 54 | ); 55 | -------------------------------------------------------------------------------- /codeForChapters/chapter 04/roundFix.ts: -------------------------------------------------------------------------------- 1 | const roundFix = (function () { 2 | let accum = 0; 3 | return (n: number): number => { 4 | // reals get rounded up or down 5 | // depending on the sign of accum 6 | const nRounded = 7 | accum > 0 ? Math.ceil(n) : Math.floor(n); 8 | 9 | console.log( 10 | "accum", 11 | accum.toFixed(5), 12 | " result", 13 | nRounded 14 | ); 15 | 16 | accum += n - nRounded; 17 | return nRounded; 18 | }; 19 | })(); 20 | 21 | roundFix(3.14159); // accum 0.00000 result 3 22 | roundFix(2.71828); // accum 0.14159 result 3 23 | roundFix(2.71828); // accum -0.14013 result 2 24 | roundFix(3.14159); // accum 0.57815 result 4 25 | roundFix(2.71828); // accum -0.28026 result 2 26 | roundFix(2.71828); // accum 0.43802 result 3 27 | roundFix(2.71828); // accum 0.15630 result 3 28 | 29 | const roundFix1 = (() => { 30 | let accum = 0; 31 | return (n: number): number => { 32 | const nRounded = 33 | accum > 0 ? Math.ceil(n) : Math.floor(n); 34 | accum += n - nRounded; 35 | return nRounded; 36 | }; 37 | })(); 38 | 39 | const roundFix2 = (accum: number, n: number) => { 40 | const nRounded = accum > 0 ? Math.ceil(n) : Math.floor(n); 41 | accum += n - nRounded; 42 | return { accum, nRounded }; 43 | }; 44 | 45 | export { roundFix, roundFix1, roundFix2 }; 46 | -------------------------------------------------------------------------------- /codeForChapters/chapter 05/answers/question_5.generating_html.ts: -------------------------------------------------------------------------------- 1 | const characters = [ 2 | { name: "Fred", plays: "bowling" }, 3 | { name: "Barney", plays: "chess" }, 4 | { name: "Wilma", plays: "bridge" }, 5 | { name: "Betty", plays: "checkers" }, 6 | { name: "Pebbles", plays: "chess" }, 7 | ]; 8 | 9 | const list = characters 10 | .filter( 11 | (x) => x.plays === "chess" || x.plays == "checkers" 12 | ) 13 | .map((x) => `
  • ${x.name}
  • `) 14 | .reduce((a, x) => [a[0] + x], [""]) 15 | .map((x) => `
      ${x}
    `) 16 | .reduce((a, x) => x); 17 | 18 | console.log(list); 19 | //
    • Barney
    • Betty
    • Pebbles
    20 | 21 | const list2 = characters 22 | .filter( 23 | (x) => x.plays === "chess" || x.plays == "checkers" 24 | ) 25 | .map( 26 | (x, i, t) => 27 | `${i === 0 ? "
      " : ""}` + 28 | `
    • ${x.name}
    • ` + 29 | `${i == t.length - 1 ? "
    " : ""}` 30 | ) 31 | .reduce((a, x) => a + x, ""); 32 | 33 | console.log(list2); 34 | 35 | const list3 = characters 36 | .filter( 37 | (x) => x.plays === "chess" || x.plays == "checkers" 38 | ) 39 | .map((x) => `
  • ${x.name}
  • `) 40 | .reduce( 41 | (a, x, i, t) => 42 | a + x + (i === t.length - 1 ? "" : ""), 43 | "