Functional Patterns with TypeScript

John Tucker
codeburst
Published in
5 min readJan 4, 2019

--

Exploring if you can have your cake and eat it too.

Earlier this week, I found myself in conflict between my personal satisfaction in using TypeScript and the dissatisfaction recently expressed by a respected leader in the JavaScript community.

TypeScript continues to fall flat on its face for most higher order functions. Maybe I just don’t know how to use it correctly (after years living with it on a regular basis — in which case, they really need to improve usability, documentation, or both), but I still don’t know how to properly type the map operation in TypeScript, and it seems to be oblivious to anything going on in a transducer. It fails to catch errors, and frequently complains about errors that aren’t really errors at all.

Eric Elliott — Top JavaScript Frameworks and Topics to Learn in 2019

Having read and written on Eric Elliott’s excellent series on functional programming with JavaScript, starting with Composing Software: An Introduction, I thought to revisit some functional patterns with TypeScript.

Functors

One interesting, and more complicated, pattern is a functor.

A functor data type is something you can map over. It’s a container which has an interface which can be used to apply a function to the values inside it. When you see a functor, you should think “mappable”. Functor types are typically represented as an object with a .map() method that maps from inputs to outputs while preserving structure. In practice, “preserving structure” means that the return value is the same type of functor (though values inside the container may be a different type).

— Eric Elliott — Functors & Categories

While JavaScript arrays with their map method, are functors, we can create our own in JavaScript (example courtesy of Eric Elliott), e.g., the objects (a and c) returned by identity function:

Edit 1/4/19: Turn out that in my first pass at this, I neglected to return a functor from the map function; big mistake. Also, added some more complex mapping functions, especially conversion that switches types (from number to string) to make the problem more challenging.

In the above JavaScript example, we deliberately misuse the c functor, trying to multiply a string by 2, and thus getting an unexpected run-time result; NaN.

Let us rewrite this code in TypeScript:

note: One of the complicated parts of TypeScript is Generics; assuming the reader is familiar with this concept.

Edit 1/4/19: Obviously, since I changed the JavaScript example, I was forced into changing the TypeScript example.

Converting this from JavaScript to Typescript amounted to creating a Mappable interface and very carefully supplying the types for various parameters and return types.

In the above TypeScript example, we again deliberately misuse the c functor, trying to multiply a string by 2. This time, however, we get a compile time error (as shown in my editor, Visual Studio Code, with a completely understandable error message.

Object Progression

Eric Elliott also describes an interesting approach to implementing increasing complexity:

Start with the simplest implementation, and move to more complex implementations only as required. When it comes to objects, that progression looks a bit like this:
Pure function -> Object > Factory -> Functional Mixin -> Class

— Eric Elliott — Functional Mixins

Earlier I wrote an article, Examples in JavaScript Functional Programming: Part 3, where I created examples of each of the patterns, in particular:

Thought to use a series of simple and consistent examples to demonstrate this guidance. For familiarity sake, all the examples involve a calculation of a line:
y = (x * slope) + intercept

— John Tucker — Examples in JavaScript Functional Programming: Part 3

Let use recreate those examples in TypeScript; exploring any challenges we encounter:

Pure Function

The simplest way to deliver reusable code is through a pure function.

A pure function is a function where the return value is only determined by its input values, without observable side effects. Some common things to avoid:

  • Do not mutate the input, e.g., Array.prototype.push().
  • Do not use (changeable) variables outside of the function’s scope.

Converting this from JavaScript to Typescript simply amounted to supplying the number type on function’s parameter (x); the return value is inferred.

In this and the following examples, we gain valuable type safety (with compile-time checks). For example, in JavaScript we could have misused myPureFunction by supplying it a string with an unexpected runtime result. In TypeScript this would have resulted in a compile-time error (visible in one’s editor).

Object

We can use an object literal to deliver the same reusable code, but adding the capability of changing the slope and intercept. It is important to note that there is only one object (a singleton).

Converting this from JavaScript to Typescript simply amounts to supplying the parameter and return types, both number, on the y method.

One interesting thing that I learned while writing this article is that with TypeScript, this is typed as any; explaining why we need to explicitly provide the return type.

Factory

We might want to have more than one such object; each with its own slope and intercept.

Converting this from JavaScript to Typescript is a little more complicated as we need to define the object’s structure using a MyObject interface; outside of this we only needed to supply the parameter and return types for the three methods setIntercept, setSlope, and y.

Functional Mixin

The additional complexity of function mixins allow you to create objects that derive functionality from multiple sources. In this example, we have two objects (objectA and objectB) that are enhanced with the functionality from myFunctionalMixin.

This is the most complicated example to convert to TypeScript as we need to use Generics. At the same time, the changes only amount to creating the MyMixedObject interface and supplying parameter and return types on several functions.

Class

Much like functional mixins, you can uses classes to create objects that derive functionality from multiple sources; really no new capability here. In this case, we create objects of type ClassA and ClassB that in-turn derive functionality from MyClass.

Unsurprisingly, converting the class example to TypeScript was trivial; simply needed to type the intercept and slope properties and provide visibility modifiers on class properties and methods.

One interesting observation is that with classes, TypeScript can property handle the this keyword; thus the method’s return types can be inferred properly.

Conclusion

In the end, I found it fairly straightforward using TypeScript with a number of functional patterns; admittedly the functional mixin example took me a bit of time to get straight.

Edit 1/4/19: The functor example turned out to be harder than I originally thought.

Bottom line, I don’t see how TypeScript interferes with one writing in a functional style. Seems that you can have your cake and eat it too.

Enjoy!

--

--