Development icon

Embracing Type Systems in JavaScript

Daniel Lemay, Developer
#Front-end Development | Posted

One of the proposed benefits of JavaScript development was the ability to move quickly and not have to worry about taking the time to set up types and interfaces. You were able to just code and let JavaScript infer intention by coercing strings to integers and so on. Unfortunately, this came with a whole host of other problems such as: undefined is not a function and cannot access property x of undefined. The ability to loosely type variables and override them with ease can result in a debugging nightmare. ES6 helps to alleviate some of these concerns via block scoping variables and the introduction of const. However, that can only go so far. The addition of a static type system can further help remedy these problems.

Screenshot of JS with static typing

Static typing in JavaScript involves declaring the types of variables, function parameters, and function returns. Type systems can provide several benefits to a JavaScript codebase. When using a type system, the compiler will throw an error if something is called with the incorrect type. This allows the developer to see the type error in their editor of choice, or by running a terminal command. The developer can then identify potential errors before runtime, where they may be more costly and difficult to pinpoint.

Types allow developers to make impossible states impossible. There are times where a variable should only be one of a predetermined allowed set of values. Without a type system the developer may create a switch statement for the various expected variable paths and provide a default case for when someone passes an incorrect variant. Unfortunately, there is no inherited documentation that accompanies the function call site with allowed variants. The developer is then left to track down how the function is used, hope the documentation is up to date or engage in a trial and error pattern. All these options waste valuable time. A type system helps to correct this issue by allowing the appropriate use of enums to allow a variable to be one of several preselected options. If a value doesn’t match the enum, the compiler will throw an error.

Enum in JS static typing

TLDR: type systems make JavaScript safer.

Documenting functions and modules across a codebase can be difficult, especially when the function declaration and usage are in separate files. Earlier attempts such as JSDoc relied on comment block annotations. Component and module based architecture encourages storing reusable functionality in separate modules to allow for drier coder. Beyond the use of enums, a type system increases discoverability in calling functions or classes from other modules. Editors can integrate with a type system and provide tool tips documenting the function. These tooltips may describe what arguments a function accepts, what type they are, and what the function returns. The end result is readily available documentation of how to use a function at the call site. There are also benefits when looking at the function declaration. The type signature of the function allows for quick review of what it accepts and returns.

Tooltip with JS static typing

TLDR: type systems are free documentation.

Type systems reduce the need to create unit tests which check the structure of the data received. These unit tests are tedious and can require significant effort to update when the data structure needs change. Using tests to assert the structure of the data is as expected is using the wrong tool for the job. Type systems, by design, excel at declaring data structures and accompanying types. They are more efficient for declaring the shape of data, and flagging any errors if there are deviations from the type. If there are common data patterns throughout an application, the type declarations can often be removed to their own module and imported into other modules where needed.

Data structures

TLDR: types systems are free unit tests.

Refactoring is where type systems shine. Similar to tests, a significant benefit of type systems comes from refactoring code and resolving errors which arise. If the developer changes a type, the compiler alerts them to all instances which use that type and now have mismatch errors. This is powerful to know where to go to resolve an issue, instead of manually checking hundreds or thousands of files.

For example, a primitive UI component may update the structure of the data that it expects to receive. Without a type system, the developer would need to search for all components which use this primitive and then update them to the new structure. If the change is significant, they may also want to manually check the various changes to confirm they are working as intended. A type system will flag all instances where the primitive is now called incorrectly. This allows the developer to know exactly which files to update, and have a greater sense of confidence that the issues have been resolved once the type error is cleared.

TLDR: type systems help us refactor with confidence.

Handling potential null and undefined values can be overlooked and the cause of many runtime errors, such as the ones mentioned above. Type systems help to alleviate this problem, by flagging if there is an unhandled case where a value may be null or undefined. For example, if an object may not exist and the developer tries to access a property on the object, a type system will throw an error. To resolve the error, the code must check that the object exists. This process helps prevent the runtime error of cannot access property x of undefined.

Type systems are a great tool to add to your developer toolkit. This added layer of static analysis, on top of good linting practices, can provide greater confidence levels in code. Type systems reduce runtime errors and make a codebase more resilient against unhandled cases. To learn more about potential type system solutions to add to your JavaScript applications, I would recommend checking out Flow or TypeScript as type systems to be added or included with a project. If you are looking for a robust type system and additional functionality like automatic currying check out Elm or ReasonML.

Daniel Lemay