Written by Nathan Sebhastian✏️
Hegel is a static type checker library that helps you identify typing errors as early as possible without actually running your code. Just like TypeScript and Flow, Hegel detects any information about type errors that exist in your code while you’re writing.
Hegel incorporates many of TypeScript and Flow’s design principles, such as having type annotations and type inferences, without introducing a new language feature like TypeScript’s enums. When using Hegel, you’re writing pure JavaScript without even needing to add a comment, as is the case with Flow.
In this guide, we’ll show how Hegel is different from both TypeScript and Flow and walk you through how to get started with Hegel in your next project.
Hegel versus TypeScript
Let’s break down some of the most notable differences between Hegel and TypeScript.
Skip type annotation
Hegel has a powerful type inference system that enables you to write fewer type annotations.
// Hegel
const promisify = fn => arg => Promise.resolve(fn(arg));
const id = promisify(x => x);
// And "upperStr" will be inferred as "Promise<string>"
const upperStr = id("It will be inferred").then(str => str.toUpperCase());
// TypeScript
const promisify = fn => arg => Promise.resolve(fn(arg));
const id = promisify(x => x);
// And "upperStr" will be inferred as "Promise<any>"
const upperStr = id("It will be inferred").then(str => str.toUpperCase());
No unexpected runtime errors
TypeScript doesn’t aim to apply a sound or provably correct type system, meaning it doesn’t guarantee that you won’t have any type errors at runtime. Hegel does the opposite, implementing a strong type system to guarantee that your code is valid.
// Hegel
const doubles: Array<number> = [Math.PI, Math.E];
// Error: Type "Array<number>" is incompatible with type "Array<number | string>"
const numbersToShow: Array<number | string> = doubles;
numbersToShow.push(42..toString(2));
const rounded = doubles.map(double => double.toFixed());
// TypeScript
const doubles: Array<number> = [Math.PI, Math.E];
const numbersToShow: Array<number | string> = doubles;
numbersToShow.push(42..toString(2));
// Uncaught TypeError: double.toFixed is not a function
doubles.map(double => double.toFixed());
Typed errors
Hegel implemented inference and annotation for functions, which enables you to understand what error is being thrown by the code.
// Type of "assertIsTrue" function is "(boolean) => undefined throws TypeError"
function assertIsTrue(arg) {
if (!arg) {
throw new TypeError("arg is invalid")
}
}
try {
assertIsTrue(false);
} catch (e) {
// Type of "e" variable is "TypeError | unknown"
}
// TypeScript
function assertIsTrue(arg) {
if (!arg) {
throw new TypeError("arg is invalid")
}
}
try {
assertIsTrue(false);
} catch (e) {
// Type of "e" variable is "any"
}
No new constructors
Unlike TypeScript, Hegel is not a superset language. That means constructors and features outside of JavaScript, such as decorators, private class fields, namespaces, enums, and other goodies from TypeScript, are not available in Hegel.
// TypeScript
enum UserStatus {
Active,
Muted,
Banned
}
class User {
constructor(
public name: string,
public status: UserStatus
) {}
}
const Anatoly = new User("Anatoly", UserStatus.Active);
// Hegel
const UserStatus = Object.freeze({
Active: "Active",
Muted: "Muted",
Banned: "Banned"
});
class User {
name: string;
status: $Values<$TypeOf<UserStatus>>
constructor(name, status) {
this.name = name;
this.status = status;
}
}
const Anatoly = new User("Anatoly", UserStatus.Active);
No type coercion and any
type
Since Hegel is concerned with implementing a sound type system, it doesn’t have type coercion or an any
type.
// Error: There is no "any" type in Hegel.
const something: any = null;
// Error: Type cast does not exist in Hegel
(null: any).call();
Hegel and Flow
Hegel shares many similarities with Flow since they are both static type checker libraries. Below are some notable differences between Hegel and Flow.
Better type inference
Flow has a hard time inferring generic types, so if you want to have the right type, annotate it. This is because Flow.js infers function type by function usage.
Hegel infers function type by function declaration. As a result, Hegel inferences polymorphic type.
// Hegel
// Type of "id" function is "<_a>(_a) => _a"
const id = x => x;
// Type of "num" variable is "number"
let num = id(4);
// Type of "str" variable is "string"
let str = id("4");
// Type of "anotherId" variable is "<_a>(_a) => _a"
let anotherId = id(id);
// Flow
// Type of "id" function is "(number | string | ((x: V$1) => V$2)) => (number | string | ((x: V$1) => V$2)"
const id = x => x;
// Type of "num" variable is "number | string | ((x: V$1) => V$2)"
let num = id(4);
// Type of "str" variable is "number | string | ((x: V$1) => V$2)"
let str = id("4");
// Type of "anotherId" variable is "number | string | ((x: V$1) => V$2)"
let anotherId = id(id);
Typed errors
Just like with TypeScript, Flow has no useful type inference for errors and will return an empty
type.
// Type of "assertIsTrue" function is "(boolean) => undefined throws TypeError"
function assertIsTrue(arg) {
if (!arg) {
throw new TypeError("arg is invalid")
}
}
try {
assertIsTrue(false);
} catch (e) {
// Type of "e" variable is "TypeError | unknown"
}
/* @flow */
function assertIsTrue(arg) {
if (!arg) {
throw new TypeError("arg is invalid")
}
}
try {
assertIsTrue(false);
} catch (e) {
// Type of "e" variable is "empty"
}
No custom library definition language
Instead of creating its own custom library definition like Flow, Hegel implemented the same d.ts definition as in TypeScript. Every library that has TypeScript definitions should work with Hegel.
Hegel is implemented in JavaScript
Flow is implemented mainly in OCaml, which makes it hard for JavaScript developers to contribute to the project.
Hegel is implemented in JavaScript so that developers who use it can help resolve any PRs or issues that arise in the future.
No type coercion and any
type
Flow has both type coercion and any
type, just like TypeScript.
// Error: There is no "any" type in Hegel.
const something: any = null;
// Error: Type cast does not exist in Hegel
(null: any).call();
Getting started with Hegel
To start using Hegel in your project, install its CLI package from the terminal. Please note that you need to have Node.js version 12 or higher.
# globally
$ npm install -g @hegel/cli
# locally
$ npm install -D @hegel/cli
Once installed, create a new index.js
file and write a variable with type annotation.
let price :number = "7"
Run the hegel
command from your project’s root directory. It will scan all .js
files for typing errors.
hegel
./index.js:1
> 1 | let price :number = "7"
| ^^^^^^^^^^^^^^^^^^^ Type "'7'" is incompatible with type "number"
And just like that, you’re all set up! You don’t need to create a .tsx
file or write @flow
comment on your file.
Setting up for production
Just like Flow, a JavaScript runtime engine such as Node will throw an error when you run the file because it doesn’t recognize the annotation syntax.
To make it run properly, you have to strip away Hegel typing syntax with either Babel or flow-remove-types.
Using Babel
Install the required Babel packages.
$ npm i -D @babel/core @babel/cli @babel/preset-flow
Write a .babelrc
file at the root of your project and use the following preset.
{
"presets": [["@babel/preset-flow", { "all": true }]]
}
Now you can run it from the terminal.
npx babel index.js -d build/
You can also add it as a script inside your package.json
.
{
"scripts": {
"build": "babel index.js -d build/",
}
}
Then, run the script.
npm run build
Using flow-remove-types
Install the package.
npm i -D flow-remove-types
Add the build script inside your package.json
, just like with Babel.
{
"scripts": {
"build": "flow-remove-types index.js --out-dir build/",
}
}
Finally, run the script.
npm run build
Conclusion
Hegel is a new static type checker library that seeks to bring together all the best parts of TypeScript by combining a static, strong type system with great type inference. It attempts to implement a minimalist but completely static type checker using pure JavaScript so you don’t need to use specific file extensions or comments to work with it.
Hegel also comes with an interactive online editor where you can test its limits. Don’t forget to check out the official Hegel documentation to learn about all its features.
LogRocket: Debug JavaScript errors easier by understanding the context
Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exavtly what the user did that led to an error.
LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier.
The post Introduction to Hegel appeared first on LogRocket Blog.
Top comments (0)