Typescript in a Functional Way
We all want to use advantages of functional paradigm in our hybrid language enviroments. But what kind of advantages are we talking about? Let's start with nested functions.
Nested Functions
For Example: We can calculate square of a number by the time we add 2 over it, in same function:
f(n)=(nĀ²)+2
or you can declare two functions ;
f1(n) = nĀ²
f2(n) = (n+2)
then apply changes in order nested ; f2(f1(n))
Function Composition makes the code more readable.
You might ask ; how come
f1(n) = nĀ²
f2(n) = (n+2)
f2(f1(n))
is more readable than f(n)=(nĀ²)+2
?
Basically we just extracted function declarations from function executions. The code reader will just read square(addTwo())
which means "add 2 to your parameter then take the square of the result". Javascript does not allow dot notation.
It will make sense if we will use a composer function which will compose our functions, then run many functions one by one. Such as;
const composed = composeMany(func1, func2, func3, func4, func5);
Compose Two Functions
Function composition is the process of combining two or more functions to produce a new function. Composing two functions produce a new function.
Composing functions together is like snapping together a series of pipes for our data to flow through.
Now let's see how to implement compose function in Typescript.
const square = (n: number) => n*n;
const addTwo = (n: number) => n+2;
const squareAndAddTwo = (n: number) => addTwo(square(n));
Still easy to understand right?
const compose = <T>(f: (x: T) => T, g: (x: T) => T) => (x: T) => f(g(x));
Let me explain this simple line in detail.
We assign a type variable called T and show it as <T>
before the outer arrow function. It means all types will be the same.
By using <T>
; both functions are limited to return the same type as input variable.
Don't need to worry about missed typed variables or functions returning wrong types.
Our compose function has two parameters
- f function which returns same type of it's input parameter x.
f: (x: T) => T
- g function which returns same type of it's input parameter x.
g: (x: T) => T
f can only get g as argument. That's why we can call it as an unary function.
In functional programing always prefer unary functions. If it's not possible to use unary functions than use binary functions(which have two . In functional programing; 90% of all functions are unary or binary(function calls with two parameters). To make our compose function more correct we can develop it even more. Let's tell typescript to limit the type of f function's argument to match the
the return type of the g function.
const compose3 = <T1, T2, T3>(f: (x: T2) => T3, g: (x: T1) => T2) => (x: T1) => f(g(x));
Now f function's argument always must be the same as return type T2 of g function.
How cool is that? Isn't compose function rock solid now? Just show this function when someone argues about typescript. Typescript is useful in such cases. you won't turn back. This is real bug-free coding. It won't even need to get compiled. If your function will return some other type then your IDE will scream.
How about composing many functions?
Typescript has rest parameters so we can use them. To have more information about rest parameters in typescript please check documantation.
https://www.typescriptlang.org/docs/handbook/functions.html#rest-parameters
As a result, here is the final code as a gist. You better bookmark it for your future needs.
Top comments (0)