Functions are treated, in TypeScript, as first-class citizens: they are objects that can be assigned to variables, and as such can be both passed as inputs to and returned as output from other functions, all while still maintaining their ability to be themselves invoked. When treated as an object, a function remains unexecuted until such a time as it has been invoked.
This provides interesting possibilities for a number of different applications. It is likely that, in even just casually using common language features, experience with functions-as-objects has already been had. Consider, for example, array manipulation functions, such as Array.prototype.map
, which converts every element of a given array into a new value by applying a mapping function to it. The callback that this function takes in is, itself, a function.
Functions can be returned from other functions. That is, upon invoking a function, the result from that function is another function. This result can be either directly invoked, assigned to a variable for later invocation, passed to another function as input, etc. Furthermore, this function could, itself, generate yet another function as its output. Functions all the way down!
Functions which do either or both of these two things—take another function as parameter or return a function as a result—are described as being "higher order functions", or HOFs. This phraseology comes from mathematics, and comes into functional programming via lambda calculus.
Recalling that each function defines a separate invocation scope which folds in the scoped context in which it was defined, HOFs provide cleaner, safer scope from which to define closures. The parameter list for a HOF, for example, can be utilized within a closure function generated by that HOF; despite the invocation of the HOF being finished, and its corresponding scope removed, some of its data persists within the function it generated.
This can allow for behavioral abstraction, wherein a behavior needed throughout an app can be defined once, parameterized, instanced, and used in multiple areas, all without having to repeat the underlying functional code. This affords both maintainability and encapsulation.
Within TypeScript, a HOF can specify a type definition for a functional input or output inline, or remove it out as a separate type; the latter is not only cleaner, but allows for better self-documentation within the code, and allows other contexts within the code to utilize that type definition. Further exploration of HOFs within TypeScript will be explored in upcoming topics, as their utility is foundational.