loading...
Cover image for Introduction to Hegel
LogRocket

Introduction to Hegel

bnevilleoneill profile image Brian Neville-O'Neill Originally published at blog.logrocket.com on ・6 min read

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.

LogRocket Free Trial Banner

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.

Try it for free.


The post Introduction to Hegel appeared first on LogRocket Blog.

Posted on by:

bnevilleoneill profile

Brian Neville-O'Neill

@bnevilleoneill

Director content @LogRocket. I didn't write the post you just read. To find out who did, click the link directly above my name.

LogRocket

LogRocket's thoughts on all things frontend.

Discussion

markdown guide