Static type inference for JavaScript.
Inside every big ugly language there is a small beautiful language trying to come out.
I have little time for this project, and don't use JS at work anymore so I'm less motivated to continue. But I still believe it's the right way to go with type safety in JS.
+
and []
operators).this
dynamic scoping rules.For more information see Infernu's Type System.
Also see the intro blog post for a short discussion comparing infernu to other type checkers.
First, install haskell stack.
Then use stack to install infernu:
git clone [email protected]:sinelaw/infernu.git
cd infernu/
stack setup
stack install
Usage: see infernu --help
Quick example usage:
echo 'function getLength(x) { return x.length; }' > getLength.js
infernu getLength.js
Output:
// getLength : a.({length: b, ..c} -> b)
function getLength(x) { return x.length; }
Then run:
cd infernu/
stack setup
stack install
The infernu
executable will be installed to your ~/.local/bin
. You may want to add it to your PATH
.
cd infernu
stack setup
stack build
cd test
./test.sh
Currently there are still a few failing tests due to unclosed issues in the type system.
The file test/fail.txt
records the test failures and is kept in git. This makes it easier to track progress on outstanding bugs.
JavaScript:
var num = 2;
var arrNums = [num, num];
Infernu infers:
// num : Number
// arrNums : [Number]
That is, an array of numbers.
Objects:
var obj = { something: 'hi', value: num };
Inferred type:
// obj : {something: String,
value: Number}
That is, an object with two properties: 'something', of type string, and 'value' of type number.
this
In JS, this
is one truly awful part. this
is a dynamically scoped variable that takes on values depending on how the current function was invoked. Infernu knows about this (pun intended) and infers types for functions indicating what this
must be.
For example:
function useThisData() {
return this.data + 3;
}
Infernu infers:
// useThisData : {data: Number, ..a}.(() -> Number)
In words: a function which expects this
to be an object with at least one property, "data" of type Number
. It takes no arguments (hence the empty ()
). It returns a Number
.
If we call a function that needs this
incorrectly, Infernu will be angry:
Error: Could not unify:
{data: Number, ..a}
With:
Undefined
Because we called useThisData
without a preceding object property access (e.g. obj.useThisData
), it will get undefined
for this
. Infernu is telling us that our expected type for this
is not unifiable with the type undefined
.
Given the following function:
function makeData(x) {
return {data: x};
}
Infernu infers the following type:
a.(b -> {data: b})
In words: A function that takes anything for its this
, and an argument of any type b
. It returns an object containing a single field, data
of the same type b
as the argument.
Given the following function:
function getData(obj) {
return obj.data;
}
Infernu infers:
h.({data: i, ..j} -> i)
In words: a function taking any type h
for this
, and a parameter that contains at least one property, named "data" that has some type i
(could be any type). The function returns the same type i
as the data property.
See here for more about Infernu's type classes.
The basic example is for the +
operator:
function add(x,y) { return x + y; }
The type for add
is inferred to be:
// add : Plus b => a.((b, b) -> b)
Meaning: given any type a
that is an instance of the Plus
type class, the function takes two a
s and returns an a
.
The two instances of Plus
currently defined are the types Number
and String
.
Noam Lewis - @noam_lewis
The following needs to be done to make infernu reasonably usable:
Things that could be done, but may not be so important:
More important but also more complicated or impossible to implement:
maybe
.obj.prototype.something = ...
- requires non-local context to deteremine the type of a constructor function.arguments
(a tuple?) and function bind
p.s. (Formerly known as Inferno / Safe JS / SJS)