What & Why
Setup
Type Annotations
Type AnnotationsPrimitive TypesUnknown TypeOptional TypesUnion TypesFunction TypesFunction ReturnsOptional and Default ArgumentRest ArgumentFunction TypesFunction typeObject TypesClass TypesArray TypesTuple TypesType AliasesGeneric TypesMagic TypesModules
Type System
ConfigurationLibraries
For Potential Contributors
Index

Function Types

Edit

Functions have two places where you can annotate types: arguments (input) and the return value (output).

Playground
// Function Declaration
function sum1(a: number, b: number): number {
return a + b;
}
// Function Expression
const sum2 = function (a: number, b: number): number {
return a + b;
};
// Arrow Function Expression
const sum3 = (a: number, b: number): number => a + b;
let result = sum1(1, 2); // 👌!
result = sum2(1, 2); // 👌!
result = sum3(1, 2); // 👌!
// Error: Type "false" is incompatible with type "number"
result = sum1(false, "");
// Error: Type "''" is incompatible with type "number"
result = sum2("", 4);
// Error: Type "'1'" is incompatible with type "number"
result = sum3("1", "2");

Function Returns

As you understand, arguments types will be checked when you try to apply arguments to a function, but return type will be checked when you implement function logic.

Playground
function awesome(name: string): string {
// Error: Type "2" is incompatible with type "string"
return 2;
}

Return types ensure that every return will be called with the same type. This prevents you from accidentally not returning a value under certain conditions.

Optional and Default Argument

All arguments are required by default, but as was mentioned in Optional Types, if you annotate some argument as optional type then this argument will become optional:

Playground
function awesome(name: ?string) {
const actualName = name === undefined ? "you" : name;
return `Awesome, ${actualName}.`;
}
let result = awesome(); // 👌!
result = awesome("JavaScript"); // 👌!

In JavaScript, we can also set a default value for optional arguments like this:

Playground
function awesome(name = "you") {
return `Awesome, ${name}.`;
}

So, which type should the name argument be annotated with? You can see this demonstrated in the Hegel Playground. If you hover over the function name and then on the argument name, you will see some magic; the function argument is string | undefined outside the function but string inside.

Playground
// awesome: (string | undefined) => string
function awesome(name: string = "you") {
return `Awesome, ${name}.`;
}
let result = awesome(); // 👌!
result = awesome("JavaScript"); // 👌!

Rest Argument

Sometimes, you don't know how many arguments will be applied to the function. JavaScript supports rest arguments. The rest operator '...' groups all applied arguments in an array of arguments.

Playground
function sum(...numbers) {
// ...
}

In Hegel you can annotate this argument the same as other arguments, but with a constraint. The type of this rest argument should be an Array type or tuple type

Playground
function sum(...numbers: Array<number>) {
return numbers.reduce((a, b) => a + b, 0);
}
let result = sum(); // 👌!
result = sum(1); // 👌!
result = sum(1, 2); // 👌!
result = sum(1, 2, 42); // 👌!
result = sum(1, 2, 42, 14); // 👌!
// ...
Playground
function sum(
...numbers: [number] | [number, number] | [number, number, number]
) {
return numbers.reduce((a, b) => a + b, 0);
}
// Error: Type "[]" is incompatible with type "...[number, number, number] | [number, number] | [number]"
let result = sum();
result = sum(1); // 👌!
result = sum(1, 2); // 👌!
result = sum(1, 2, 3); // 👌!
// Error Type "[1, 2, 3, 4]" is incompatible with type "...[number, number, number] | [number, number] | [number]"
result = sum(1, 2, 3, 4);

Function Types

Sometimes, you need to annotate arguments or return types as a function. Hegel has syntax for function type annotation (type1, type2) => returnType. For example:

Playground
function twice(fn: (number) => number, arg: number) {
return fn(fn(arg));
}
let result: number = 0;
result = twice((a: number): number => a + 4, 4); // 👌!
result = twice(
// 👌!
(a: number | string): number => (typeof a === "string" ? parseInt(a) : a) + 4,
4
);
// Error: Type "(string) => number" is incompatible with type "(number) => number"
result = twice((a: string): number => parseInt(a), 4);
// Error: Type "(number) => string" is incompatible with type "(number) => number"
result = twice((a: number): string => String(a), 4);
// Error: Type "(number) => number | string" is incompatible with type "(number) => number"
result = twice((a: number): number | string => a, 4);

If you play with the previous example you will see the next rule of the function type: You can assign one function to another only if the actual argument types are wider than declared and the return type is more specific than declared. This rule sounds like: function is contravariant by arguments and covariant by return.

A more classical and simple example of this rule:

Playground
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {}
class Corgi extends Dog {}
function presentMyDog(goodBoy: Dog, dogPresenter: (Dog) => Dog): string {
const dog = dogPresenter(goodBoy);
return `${dog.name}, my ${dog.name}, i will give you to noone.`;
}
const nickolay = new Corgi("Nickolay");
let result = "";
result = presentMyDog(nickolay, (goodBoy: Dog): Dog => goodBoy); // 👌!
result = presentMyDog(nickolay, (goodBoy: Animal): Dog => nickolay); // 👌!
// Error: Type "(Corgi) => Dog" is incompatible with type "(Dog) => Dog"
result = presentMyDog(nickolay, (goodBoy: Corgi): Dog => nickolay);
result = presentMyDog(nickolay, (goodBoy: Dog): Corgi => nickolay); // 👌!
// Error: Type "(Dog) => Animal" is incompatible with type "(Dog) => Dog"
result = presentMyDog(nickolay, (goodBoy: Dog): Animal => nickolay);

Function type

Also, Hegel supports Function type which represents any possible function in JavaScript:

Playground
function argsLength(fn: Function) {
return fn.length;
}
let length = 0;
length = argsLength(() => 2);
length = argsLength((a: number) => a);
length = argsLength(parseFloat);
// Error: Type "2" is incompatible with type "Function"
length = argsLength(2);
// Error: Type "[]" is incompatible with type "Function"
length = argsLength([]);