What & Why
Setup
Type Annotations
Type System
Type CompatibilityWhich type is assignable to another?Function types CompatibilityGenerics CompatibilitySoundnessHegel IssueType InferenceType Refinement
ConfigurationLibraries
For Potential Contributors
Index

Type Compatibility

Edit

The Hegel tries to implement type safety in JavaScript via strong type system. It means that analyzer ensure that variable value is always assignable to declarated (or inferenced) static type.

Which type is assignable to another?

Next hierarchy is presented in Hegel.

This hierachy defines rules of subtyping: "Every type which is higher in the hierarchy will be a super type for current type". Lets discover that by examples.

Playground
// Tuple Type
const tuple: [number, number] = [42, 24]; // 👌!
// This is also an Array
const array: Array<number> = [42, 24]; // 👌!
// This is also an Object
const object: Object = [42, 24]; // 👌!
// This is also "unknown"
const unknown: unknown = [42, 24]; // 👌!
Playground
// But not in reverse order
// Object is not a Tuple
// Error: Type "Object" is incompatible with type "[number, number]"
const tuple: [number, number] = new Object();
// And object is not a Array
// Error: Type "Object" is incompatible with type "Array<number>"
const array: Array<number> = new Object();
// But object is Object
const object: Object = new Object();
// And object is unknown
const unknown: unknown = new Object();

Function types Compatibility

Functions does not create any hierarhy. Compatibility of two functions defined by the rule: you can assign function to another only if actual arguments types are wider then declared and return type is more specific then declared. This rule sounds like: function is contravariant by arguments and covariant by return.

Playground
let func: (number) => number = () => 42;
func = (a: number): number => a; // 👌!
// It's okay because "number | string" is wider than "number"
func = (a: number | string): number => 44; // 👌!
// It's okay because "42" is more specific than "number"
func = (a: number): 42 => 42; // 👌!
// Error: Type "(42) => number" is incompatible with type "(number) => number"
// Because "42" is more specific than "number"
func = (a: 42): number => 42;
// Error: Type "(number) => number | string" is incompatible with type "(number) => number"
// Because "number | string" is wider than "number"
func = (a: number): number | string => 42;

Generics Compatibility

Two generic may be contained in hierarchy of each other only if their actual type parameter is in heierarchy of each other. For example, imagine generic container class:

Playground
class Container<T> {
value: T;
constructor(value) {
this.value = value;
}
}

We can see a the same subtyping behavior as with primitive types:

Playground
const wrapped42: Container<42> = new Container<42>(42); // 👌!
const wrappedNumber: Container<number> = new Container<42>(42); // 👌!
// Error: Type "Container<number>" is incompatible with type "Container<42>"
const wrappedNumber42: Container<42> = new Container<number>(84);
// Error: Type "Container<number>" is incompatible with type "Container<'Hello, World'>"
const wrappedHelloWorld: Container<"Hello, World"> = new Container<number>(42);

It's because Container type parameters are contained in hierarchy of each other.

But, as you can see, only literals were used for examples. It's because every subtype of Object in JavaScript act not like a value but like a "pointer" at value. So, for all reference types you can't do assign between variables if variables are not the same type.

Playground
const wrapped42: Container<42> = new Container<42>(42); // 👌!
// Error: Type "Container<42>" is incompatible with type "Container<number>"
const wrappedNumber: Container<number> = wrapped42;

Soundness

Soundness is an ability of system to guarantee that after analysis your program will not be in invalid state.

Hegel tries to implement the same soundness type system as ReasonML/OCaml, Haskell, Elm and other ML -based languages. It means Hegel tries to catch every single error that might happen at runtime without runtime type checking.

If you familiar with Rust Language you may know that Rust doesn't provide any type information in runtime and all types will be stripped after type checking, but Rust program is still type safe. It's our dream to implement the same type checking in JavaScript.

Hegel Issue

Also, Hegel as other tools has hack for ignoring any kind of errors. You can pass the "@hegel-issue" comment and error will be ignored.

Playground
// @hegel-issue
const a: string = 44;

But, it's NOT RECOMMENDED to use it for a quick fix or business task solution. "@hegel-issue" should be used only if the error is Hegel bug.