First blog post!

posted by Giovanni Ravalico 2020-07-25

Hello

Welcome to suddenlyGiovanni, the personal blog of Giovanni Ravalico.

More to come soon…

This is a test snippet of code to verify the code box highlighting capabilities:

1function assert<T extends unknown>(
2 condition: T,
3 message?: string
4): T extends false ? never : void {
5 if (!condition) {
6 throw new Error(
7 message || `Assertion failed, condition was ${Boolean(condition)}`
8 )
9 }
10 return undefined
11}
12
13/**
14 * Prepend
15 * It adds a type E at the top of a tuple T
16 */
17type Prepend<E, T extends any[]> = ((head: E, ...args: T) => any) extends (
18 ...args: infer U
19) => any
20 ? U
21 : T
22
23/** Tail conveniently removes the first entry that a tuple might contain. */
24type Tail<T extends any[]> = ((...t: T) => any) extends (
25 _: any,
26 ...tail: infer TT
27) => any
28 ? TT
29 : []
30
31/**
32 * Drop
33 * It takes a tuple T and drops the first N entries.
34 * The Drop type will recurse until Length<;I> matches the value of N that we passed.
35 * In other words, the type of index 0 is chosen by the conditional accessor until that condition
36 * is met.
37 * And we used Prepend<any, I> so that we can increase a counter like we would do in a loop.
38 * Thus, Length<I> is used as a recursion counter, and it is a way to freely iterate with TS.
39 */
40type Drop<N extends number, T extends any[], I extends any[] = []> = {
41 0: Drop<N, Tail<T>, Prepend<any, I>>
42 1: T
43}[Length<I> extends N ? 1 : 0]
44
45/**
46 * Cast
47 * requires TS to re-check a type X against a type Y, and type Y will only be enforced if it fails
48 */
49type Cast<X, Y> = X extends Y ? X : Y
50
51/** Length */
52type Length<T extends any[]> = T['length']
53
54/**
55 * this interface and all the helper fns belong to `Pierre-Antoine Mills`
56 * @author @pirix-gh
57 */
58type Curry<P extends any[], R> = <T extends any[]>(
59 ...args: Cast<T, Partial<P>>
60) => Drop<Length<T>, P> extends [any, ...any[]] // @ts-ignore
61 ? Curry<Cast<Drop<Length<T>, P>, any[]>, R>
62 : R
63
64const _curry = (fn: Function) => {
65 const curried = function (...t: any[]) {
66 return t.length >= fn.length
67 ? fn.call(this, ...t)
68 : curried.bind(this, ...t)
69 }
70 return curried
71}
72
73export const curry = <P extends any[], R>(
74 f: (...args: P) => R
75): Curry<P, R> => {
76 return _curry(f)
77}
78
79// test
80const toCurry = (
81 name: string,
82 age: number,
83 single: boolean,
84 ...nicknames: string[]
85) => true
86const _curried = curry(toCurry) // Curry<[string, number, boolean, ...string[]], boolean>
87const test01 = _curried('jane', 26)(true, 'jj', 'jini') // boolean
88const test02 = _curried('jane')(26, true, 'jj', 'jini') // boolean
89// @ts-expect-error
90const test03 = _curried('jane')(26)(true, 'jj', 90000) // error: Argument of type '90000' is not assignable to parameter of type 'string'
91
92const add4 = (a: number, b: number, c: number, d: number): number =>
93 a + b + c + d
94const curriedAdd4 = curry(add4)
95assert(curriedAdd4(1, 2, 3, 4) === 10)
96assert(curriedAdd4(1, 2, 3)(4) === 10)
97assert(curriedAdd4(1, 2)(3)(4) === 10)
98assert(curriedAdd4(1)(2)(3)(4) === 10)
99assert(curriedAdd4(1)(2)(3, 4) === 10)
100assert(curriedAdd4(1)(2, 3, 4) === 10)
101assert(curriedAdd4(1, 2)(3, 4) === 10)

© 2021 Giovanni Ravalico