The new library Hegel strives to be an advanced static type checker for JavaScript. Hegel claims to provide a sound type system with strong type inference. It is still in alpha and can be experimented with in a dedicated online playground.
Hegel is a type checker for JavaScript with optional type annotations. There are no new programming language constructs to learn, as in the case of TypeScript. However, the annotation syntax must be learned. Hegel strives to prevent runtime type errors by using a strong, sound type system:
const numbers: Array<number> = [];
// HegelError: Type "Array<number>" is incompatible with type "Array<number | string>"
const numbersOrStrings: Array<string | number> = numbers;
numbersOrStrings[1] = "Hello, TypeError!";
// HegelError: Property "toFixed" does not exist in "Number | undefined"
numbers[1].toFixed(1);
The previous code with TypeScript (v3.8.3) compiles without any error, but may trigger errors at runtime.
Besides the sound type system, Hegel features a strong type inference as a design goal:
// Hegel will infer "promisify" as "<_q, _c>((_c) => _q) => (_c) => Promise<_q>"
const promisify = fn => arg => Promise.resolve(fn(arg));
// There, Hegel will infer "<_c>(_c) => Promise<_c>"
const id = promisify(x => x);
// And "upperStr" will be inferred as "Promise<string>"
const upperStr = id("It will be inferred").then(str => str.toUpperCase());
// Finally, "twiceNum" will be inferred as "Promise<number>"
const twicedNum = id(42).then(num => num ** 2);
Running the same example through TypeScript (tested on version 3.7.5) will detect three errors and infer a Promise<any>
type for the result
variable. The strong type inference thus may allow developers to resort less often to annotating their code. This, in turn, often results in more readable code.
Hegel also types exceptions:
function assert(age) {
if (typeof age !== "number") {
throw new TypeError("Age is not number.");
}
if (age <= 0) {
throw new ReferenceError("Age can't be less or equals zero.");
}
}
try {
assert(0);
} catch(error) {
// The "error" variable will be typed as "ReferenceError | TypeError | unknown"
}
A side-effect of Hegel’s sound type system is the absence of type coercion and of the 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’s documentation includes a comparison of Hegel with the main alternatives TypeScript and Flow. In addition to standard types (primitives, function, object, classes, array), Hegel’s type system features an unknown type (returned for instance by JSON.parse()
), optional types, union types, tuple types, type aliases, generic types, and magic types.
Magic types help extract or create new types from existing types. Magic types are thus akin to functions taking a type into another one. Hegel defines 17 magic types. An example of the $Exclude magic type (with semantics similar to the TypeScript equivalent Exclude type) is as follows:
type Status = "Ok" | "Failed" | "Pending" | "Canceled";
// IntermediateStatuses = "Canceled" | "Panding"
type IntermediateStatuses = $Exclude<Status, "Ok" | "Failed">;
const intermediateStatuses: Array<$Exclude<Status, "Ok" | "Failed">> = ["Pending", "Canceled"];
// Error: Type "['Failed']" is incompatible with type "...Array<'Canceled' | 'Pending'>"
intermediateStatuses.push("Failed");
Devon Govett, author of @parceljs, reacted on Twitter:
Looks really interesting! Closer to Flow than TS, but implemented in JS. Focused on type inference, soundness, just JS with types (no additional features), supports .d.ts, vscode integration, etc.
Hegel is distributed on npm, and comes with a command-line interface and an interactive online playground. A recent version of node.js is required (>12). Installation instructions can be found in the GitHub repository.
Hegel is MIT-licensed. Feedback and contributions are welcome and may be provided via the GitHub project. The author states:
Hegel is developed by [the] community for [the] community. So, your PRs and issues will not be ignored or skipped.