
TypeScript Interfaces and JavaScript Higher-Order Functions
Code can be said to be decoupled when your components are designed in such a way that they don’t depend on the concrete implementations of others. Components are loosely coupled when dependencies between them are enforced by a type of code agreement or contract to make the communication lines between them are as simple as possible.
What follows is an explanation of how we can achieve this with both interfaces and higher-order functions.
An interface is a structure that defines the contract in your application. Classes that are derived from an interface must follow the structure provided by their interface.
A higher-order function is a function that receives a function as an argument or returns the function as output.
An interface is defined with the keyword interface and it can include properties and method declarations:
1//ProductRepository.d.ts 2export interface ProductRepository { 3 createProduct: (name: string, price: number) => boolean 4 getProduct: (id:number) => Product 5}
.d.ts files are declaration files that contain only type information. These files don't produce .js outputs; they are only used for type checking.
In the above example, the ProductRepository interface includes two method declarations, createProduct and getProduct using an arrow function that includes parameters and return type.
An interface in TypeScript can also be used to define a type
1//Product.d.ts 2export type Product = { 3 name: string 4 price: number 5}
1//Product.d.ts 2export interface Product { 3 name: string 4 price: number 5}
Let’s use this a class implementation:
Using TypeScript and VS Code, we immediately notice that the interface does its job and informs us that the implementation is incomplete. The 2 methods are missing.
Let’s fix this.
Let’s see how we can use the interface to write the ProductRepository in a more functional way using a higher-order function.
Because interfaces can be used for custom types, it is used for declaring the function return type:
Let’s fix the code
Let’s try this in plain JS. JavaScript inheritance is based on objects, not classes so we can’t use interface inheritance in JS. We have no choice but to use a higher-order function.
So let’s copy and paste this code into a .js file and see what happens:
Ok, so we have to remove the ProductRepository annotation and replace it with a JSDoc annotation.
and we’re back to where we were, just this time in plain JS. Great if you want to have the power of IntelliSense and type checking without the compilation and configuration overhead of TypeScript
Let’s now complete the code to remove the errors.
Conclusion:
When working with TypeScript you have the choice of interfaces or higher-order functions to enforce structure and decoupling.
The same can be done for JavaScript with higher-order functions using JSDoc and TypeScript type declaration files.