JSurly hates your JavaScript

And you'll be better off for it.

Download JSurly

All platforms. Version 0.5.

Logo

What is JSurly?

JSurly is a static analysis tool that prohibits ugly parts of JavaScript, and enforces some clean-code conventions. As a result, JSurly offers 100% static typing, and a wealth of error detection capabilities.

What isn’t JSurly

  • A new language. JSurly hates some parts of JavaScript and disallows them, but no new keywords or runtime behaviors are added.
  • A translator, compiler, transpiler, or any other way to say “code-generator”. The code you write is the code you run and see in debugging sessions.
  • A framework. JSurly adds no extra code to your project, nor does it add any dependencies.
  • An editor. Following JSurly’s code structure means popular editors you already know can offer perfect code inspection and autocomplete.
  • A style linter. JSurly doesn't care where you place your braces, or how many spaces/tabs you put before things. JSurly does not require any conventions that conflict with JSLint.

Running JSurly

The primary way to run JSurly is via node.js.

node jsurly.js /path/to/javascript

Passing a path to a directory will process all .js files in the specified directory and all subdirectories. Passing a path to a file processes only that file.

Language Features

Because JSurly does not modify its inputs, there are no new language features. JSurly does, however, reject several existing features, including:

  • with statements
  • function statements
  • this keywords
  • for..in loops
  • regexp literals
  • referencing functions by name
  • coercing equality operators
  • global variables defined within a function
  • using null/undefined for anything except comparison

Types

JSurly uses a new type system that is compatible with JavaScript, but offers many more opportunities for static analysis. The following sections describe types as JSurly sees them. These types are inferred for the purposes of static analysis; runtime behavior is entirely unaffected, and is carried out using the normal JavaScript type system.

JSurly only likes 100% statically-typed code. Because JSurly also hates excessive clutter, explicit type specifications are only required for function argument delcarations. Types for everything else are inferred, including: variable types, function return types, object member types, and expression result types. If any type cannot be deduced, an error is produced. Function argument types are specified using a type-specification comment. These comments must be placed just inside the function body, and begin with a /// or /**. The comment contains a type name for each argument in the list.

The type names can be specified in Namespace.Type format. The global namespace is named "global". If the type name exists in the current namespace or the global namespace (not both), the Namespace and dot may be omitted.

function myFunction (a, b, c) { /// String, TypeOne, MYNAMESPACE.TypeTwo
}

You may apply type-specification comments to other entities (such as variables and function return types) if you wish to be more explicit, but it's not required.

Namespaces

User-defined types must be contained in a namespace. Namespaces are stored in global variables, and they must be created by an immediately-invoked function that extends either the existing namespace or an empty object.

var TEST = (function (TEST) {
//
// Namespace contents
//
return TEST; }(TEST || {}));

This format allows a single namespace to be populated by code in many different files, without requiring any partiular order.

User-Defined Types

Apart from the builtin types provided by the language and runtime environment, all types are user-defined types. A type is fully-defined by its constructor, which is a function that returns an instance of the type as its last statement.

Instance Types

Instance types are created by storing the constructor as a member of the namespace.

var TEST = (function (TEST) { TEST.Counter = function (limit) { /// Number
    
var count = Math.floor(Math.random() * limit);

    
return {
        
increment: function () {
            
count++;
            
return count;
        },
        
decrement: function () {
            
count--;
            
return count;
        },
        
isOverLimit: function () {
            
return count > limit;
        },
        
limit: limit
    
};
};
return TEST; }(TEST || {}));

The preceding code creates a new type named Counter with four members. User-defined type members are alwayas read-only; attempting to assign to a member produces an error. Thus, in the example above, the counter limit may not be modified externally.

Many instances of the Counter type can exist, each created by invoking the constructor:

var counter = TEST.Counter(7);

The new keyword must not be used to create instances of user-defined types, and will produce an error.

Static Types

Static types are created by immediately-invoking a constructor and storing the result as a member of the namespace.

var TEST = (function (TEST) { TEST.Favorite = (function () {
    
var number = (1 + 2 + 3) / 2;

    
return {
        
drink: "Cider",
        
color: "Pale" + " " + "Blue",
        
number: number,
        
asciiCode: "!".charCodeAt(0)
    };
}());
return TEST; }(TEST || {}));

There is now a single instance of the Favorite type, accessible via TEST.Favorite.

Collections

Collections store multple elements, each identified by a particular type of key. Collection elements may be accessed using bracket notation myCollection[myKey], not property notation myCollection.myKey.

Array<> and ReadOnlyArray<>

Arrays contain a collection of elements identified by keys of the type Number. Values of the Array<> type are created with a JavaScript array literal. These values may be converted implicitly to ReadOnlyArray<> (which incurs no runtime cost; remember JSurly only performs static analysis).

Arrays have a subtype that indicates the type of elements they contain. The type of element can be inferred by initializing the array contents (a), assigning an element (b), pushing an element (c), passing to a function (d), or storing the array in a variable of an array type (e and f).

Values of the type Array<> may be used wherever values of the type ReadOnlyArray<> are required (g). User-defined types automatically expose any Array<> members as ReadOnlyArray<> (see JSurly output for a through g).

var TEST = (function (TEST) { TEST.Sample = function () {
    
var a = [ 1, 2, 1 + 2 ],
        
b = [],
        
c = [],
        
d = [],
        
e = [], /// Array<Number>
        
f, /// Array<String>
        
g,
        
f1,
        
f2,
        
f3;

    
f1 = function (input) { /// Array<String>
        
return input[5];
    };

    
f2 = function () {
        
b[0] = "Hello";
    };

    
f3 = function (input) { /// ReadOnlyArray<String>
        
return input;
    
};

    
c.push(7);
    
f1(d);
    
f = [];
    
g = f3(b);
    
f2();

    
return {
        
a: a,
        
b: b,
        
c: c,
        
d: d,
        
e: e,
        
f: f,
        
g: g
    };
};
return TEST; }(TEST || {}));

Dictionary<> and ReadOnlyDictionary<>

Dictionaries contain a collection of elements identified by keys of the type String. Values of the Dictionary<> type are created with a call to Object.create(null). These values may be converted implicitly to ReadOnlyDictionary<> (which incurs no runtime cost; remember JSurly only performs static analysis).

Dictionaries have a subtype that indicate the type of elements they contain. The type of element can be inferred by assigning an element (a), passing to a function (b), or storing the dictionary in a variable of a dictionary type (c and d).

Values of the type Dictionary<> may be used wherever values of the type ReadOnlyDictionary<> are required (e). User-defined types automatically expose any Dictionary<> members as ReadOnlyDictionary<> (see JSurly output for a through e).

var TEST = (function (TEST) { TEST.Sample = function () {
    
var a = Object.create(null),
        
b = Object.create(null),
        
c = Object.create(null), /// Dictionary<String>
        
d, /// Dictionary<Number>
        
e,
        
f1,
        
f2,
        
f3;

    
f1 = function (input) { /// Dictionary<Number>
        
return input["x"];
    };

    
f2 = function () {
        
a["key"] = "Hello";
    };

    
f3 = function (input) { /// ReadOnlyDictionary<Number>
        
return input;
    };

    
f1(b);
    
d = Object.create(null);
    
e = f3(b);
    
f2();

    
return {
        
a: a,
        
b: b,
        
c: c,
        
d: d,
        
e: e
    };
};
return TEST; }(TEST || {}));

Functions

Values of the Function<> type are created with a JavaScript function expression. Functions have subtypes that indicate the type of return value, and the type of each argument.

var TEST = (function (TEST) { TEST.Sample = function () {
    
var a,
        
b,
        
c,
        
d;

    
a = function (input) { /// String
        
console.log(input);
    };

    
b = function () {
        
return "Hello";
    };

    
c = function (input) { /// Array<String>
        
return input.length;
    };

    
d = function (callback) { /// Function<Boolean, Number>
        
return callback(42) ? "Yep" : "Nope";
    };

    
return {
        
a: a,
        
b: b,
        
c: c,
        
d: d
    };
};
return TEST; }(TEST || {}));

JSurly will complain about any scope variables that are never used, including function arguments. Ideally, unused arguments should be used or deleted (as JavaScript is fine with functions declaring fewer arguments than are passed in). If you absolutely must include a function argument that will not be used, it may be given a name beginning with "unused", to be ignored by the unused-variable check.

Templates

Instead of specifying an exact type, type specification comments can include "variable types" that allow any type to be substituted in their place. Types that contain "variable types" are called templates. This does not compromise static typing; the template is re-inspected for each unique combination of "variable types" used in the program. Additionally, this does not incur any increase in code size or runtime cost; remember, JSurly only peforms static analysis.

Within the template, variable types are specified by a question mark, followed by a decimal number that indicates their position in the subtype list (beginning at zero). To consume a template, the variable types are specified using the normal subtype syntax, as with arrays, functions, etc.

var TEST = (function (TEST) { TEST.Stack = function () {
    
var items = [];

    
return {
        
push: function (item) { /// ?0
            
items.push(item);
        },
        
pop: function () {
            
return items.pop();
        },
        
indexWhere: function (test) { /// Function<Boolean, ?0>
            
var index;

            
for (index = 0; index < items.length; index++) {
                
if (test(items[index])) {
                    
return index;
                }
            }

            
return -1;
        }
    };
};
return TEST; }(TEST || {}));

var TEST  = (function (TEST) { TEST.EntryPoint = (function () {
    
var stack = TEST.Stack(), /// Stack<String>
        
item;

    
stack.push("hello");
    
item = stack.pop();

    
// Error:
    // stack.push(7);

    
return {
        
a: stack,
        
b: item,
        
c: stack.indexWhere
    
};
}());
return TEST; }(TEST || {}));

Errors

Why wait until runtime to find problems?

001 - Invalid token
The specified character is not the beginning of any valid token. Extended token names are not currently supported.
var TEST = (function (TEST) { TEST.Type = function () {

    
@

    
return {};
};
return TEST; }(TEST || {}));
002 - Unexpected token
The specified token may not appear in its current position. Remove the token or insert a previously-omitted separator.
var TEST = (function (TEST) { TEST.Type = function () {

    
if (false) [
    }

    
return {};
};
return TEST; }(TEST || {}));
003 - Malformed namespace
The namespace does not adhere to the namespace format. Adjust the statement to follow the pattern: "var NAMESPACE = (function (NAMESPACE) { /* Content */ return NAMESPACE; }(NAMESPACE || {}));".
var TEST = (function (TEST) { TEST.Type = function () {

    
return {};

};
return TEST; }(TEST));
004 - Invalid namespace content
The namespace contains a statement other than an assignment to a member of the namespace. Move statements that don't populate the namespace into a type.
var TEST = (function (TEST) {

    
var x = Math.random();

return TEST; }(TEST || {}));
005 - Invalid static type
The specified static type is not an immediately-invoked function with zero arguments. Remove any arguments being declared or passed in.
var TEST = (function (TEST) { TEST.Type = (function (number) {

    
return {};

}(
Math.random())); return TEST; }(TEST || {}));
006 - Invalid instance type
The specified instance type is not a constructor function expression. Only store function expressions within a namespace object.
var TEST = (function (TEST) { TEST.Type = {

    
member: 5

}; return TEST; }(TEST || {}));
007 - Duplicate type definition
The specified type has the same fully-qualified name as a previously-defined type. Remove or rename one of the two types.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
number: 7
    
};

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
string: "hello"
    
};

};
return TEST; }(TEST || {}));
008 - Missing constructed object
Constructor does not end with a newly-constructed object being returned. Make the last statement of the constructor return an object literal.
var TEST = (function (TEST) { TEST.Type = function () {

    
var x = Math.random();

};
return TEST; }(TEST || {}));
009 - Invalid member name type
Member named using something other than an identifier. Name type members using an identifier, not a literal.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
"memberName": 7
    
};

};
return TEST; }(TEST || {}));
010 - Duplicate member definition
The specified member has the same name as a previously-defined member of the same type. Remove or rename one of the two members.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
quantity: 7,
        
name: "hello",
        
quantity: 12
    
};

};
return TEST; }(TEST || {}));
011 - Conflicting collection types
While inferring the type of collection, both Array and Dictionary types were found. Do not mix collection types within a single variable.
var TEST = (function (TEST) { TEST.Type = function () {

    
var collection = [];
    
collection = Object.create(null);

    
return {
        
quantity: 12
    
};

};
return TEST; }(TEST || {}));
012 - Conflicting element types
While inferring the type of collection elements, multiple element types were found. Do not mix element types within a single collection.
var TEST = (function (TEST) { TEST.Type = function () {

    
var f,
        
collection;

    
f = function () {
        
collection[5] = "Five";
        
collection[6] = 6;
    };

    
collection = [];
    
f();

    
return {};

};
return TEST; }(TEST || {}));
013 - Ambiguous type specification
The specified type name exists in both the current namespace and the global namespace. Specify the namespace within the type-specification comment.
var TEST = (function (TEST) { TEST.String = function () {

    
return {
        
isTwine: true
    
};

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Spool = function (string) { /// String

    
return {
        
content: string
    
};

};
return TEST; }(TEST || {}));
014 - Undefined type
The specified type does not exist. Specify a different type, or create the missing type.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
appendString: function (string) { /// Strung
        
}
    };

};
return TEST; }(TEST || {}));
015 - Malformed type specification
The type-specification comment is not well-formed. Fix any mismatched < and > markers.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
sum: function (array) { /// Array<Number>>
        
}
    };

};
return TEST; }(TEST || {}));
016 - Invalid member type
The type to the specified member cannot be exposed as a type-member (e.g. void). Assign a different type to this member, or do not expose this member.
var TEST = (function (TEST) { TEST.Type = function () {

    
var func = function () {
        
return;
    };

    
return {
        
member: func()
    };

};
return TEST; }(TEST || {}));
017 - Anonymous type.
An anonymous type was created. Construct actual types for logical objects, and use Object.create(null) for dictionaries.
var TEST = (function (TEST) { TEST.Type = function () {

    
var something = {};

    
return {
        
member: something
    
};

};
return TEST; }(TEST || {}));
018 - Premature constructor termination
The constructor may exit before reaching the final statement. Ensure the constructor only exits by returning a newly-constructed object as its last statement.
var TEST = (function (TEST) { TEST.Type = function () {

    
if (Math.random() === 42) {
        
return 5;
    }

    
return {
        
member: 7
    
};

};
return TEST; }(TEST || {}));
019 - Invalid return type
The specified return value is not valid. Ensure any collection types are fully-specified before returning.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
method: function () {
            
return [];
        }
    };

};
return TEST; }(TEST || {}));
020 - Conflicting return types
While inferring the return type, multiple types were encountered. Ensure all return statements return the same type.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
method: function () {
            
if ((Math.random() % 2) === 0) {
                
return 1;
            }
else {
                
return "Two";
            }
        }
    };

};
return TEST; }(TEST || {}));
021 - Invalid condition expression
A condition resolves to a type other than Boolean. Adjust the condition expression to produce a Boolean value.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
method: function () {
            
if ("true") {
                
return true;
            }
else {
                
return false;
            }
        }
    };

};
return TEST; }(TEST || {}));
022 - Invalid deletion target
Delete was applied to something other than a dictionary member access. Apply delete to a dictionary member, specified using bracket notation.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
method: function () {
            
var string = "asdf";
            
delete string[5];
        }
    };

};
return TEST; }(TEST || {}));
023 - Modification of read-only collection
An attempt was made to modify a read-only collection. Mutate a copy of the collection, or adjust the original collection type to be mutable.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
insertElement: function (input) { /// ReadOnlyArray<String>
            
input[2] = "Two";
        }
    };

};
return TEST; }(TEST || {}));
024 - Invalid key type
A non-String type was used as a dictionary key. Adjust the key expression to produce a string, possibly using toString().
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
method: function () {
            
var array = [ 0, 1 ];
            
array["Two"] = 2;
        }
    };

};
return TEST; }(TEST || {}));
025 - Obscured variable
A newly-defined variable hides a previously-defined variable. Rename or remove one of the two variables.
var TEST = (function (TEST) { TEST.Type = function () {

    
var thing = 7,
        
func;

    
func = function () {
        
var thing = "Value";
        
return thing;
    };

    
return {};

};
return TEST; }(TEST || {}));
026 - Invalid single-type specification
A type specification comment did not specify a valid single type. Remove any additional types in the specification.
var TEST = (function (TEST) { TEST.Type = function () {

    
var string; /// String, Number

    
return {};

};
return TEST; }(TEST || {}));
027 - Ambiguous variable type
A variable was initialized from a source that may output different types. Specify the variable type explicitly using a type-specification comment.
var TEST = (function (TEST) { TEST.Type = function () {

    
var element = window.document.getElementById("id");

    
return {};

};
return TEST; }(TEST || {}));
028 - Invalid assignment
A variable was assigned a value of an unacceptable type. Ensure variable is assigned values that match the variable type, and are not void.
var TEST = (function (TEST) { TEST.Type = function () {

    
var value = 7;

    
value = "Seven";

    
return {};

};
return TEST; }(TEST || {}));
029 - Invalid switch expression
A switch statement has an invalid input type. Ensure the switch statement switches on a fully-specified, non-void type.
var TEST = (function (TEST) { TEST.Type = function () {

    
var func = function () {
        
console.log("hello");
    };

    
switch (func()) {
        
case 1:
            
break;
    }

    
return {};

};
return TEST; }(TEST || {}));
030 - Invalid case expression
A case expression did not match the switch argument type. Adjust the case expression or switch input such that their types are compatible.
var TEST = (function (TEST) { TEST.Type = function () {

    
var input = "hello";

    
switch (input) {
        
case 1:
            
break;
    }

    
return {};

};
return TEST; }(TEST || {}));
031 - Invalid use of placeholder type
Placeholder types null and undefined may only be used in equality/inequality comparisons. Remove the null/undefined literal.
var TEST = (function (TEST) { TEST.Type = function () {

    
var variable;

    
variable = undefined;

    
return {};

};
return TEST; }(TEST || {}));
032 - Undefined variable
An attempt was made to use a variable that does not exist. Define the variable in the current or parent scope, or remove the variable reference.
var TEST = (function (TEST) { TEST.Type = function () {

    
missing = 10;

    
return {};

};
return TEST; }(TEST || {}));
033 - Invalid comparison
Two values with differing types were compared for exact equality/inequality. Replace the comparison arguments with values of the same type.
var TEST = (function (TEST) { TEST.Type = function () {

    
var first = 7,
        
second = "7",
        
equal;

    
if (first === second) {
        
equal = true;
    }
else {
        
equal = false;
    }

    
return {};

};
return TEST; }(TEST || {}));
034 - Prohibited comparison operator
The type-coercing comparison operators were used. Use the exact comparison operators, === or !==.
var TEST = (function (TEST) { TEST.Type = function () {

    
var first = 7,
        
second = 7,
        
equal;

    
if (first == second) {
        
equal = true;
    }
else {
        
equal = false;
    }

    
return {};

};
return TEST; }(TEST || {}));
035 - Invalid operator inputs
An operator was passed inappropriate types. Adjust the types according to match the types specified in the error message.
var TEST = (function (TEST) { TEST.Type = function () {

    
var first = 1,
        
second = [2],
        
third = first + second;

    
return {};

};
return TEST; }(TEST || {}));
036 - Unsupported element access
An indexer was applied to a type that does not support indexing. Apply the indexing operator to a collection, or use dot notation for non-collections.
var TEST = (function (TEST) { TEST.Type = function () {

    
var number = 1;

    
number[2] = 3;

    
return {};

};
return TEST; }(TEST || {}));
037 - Global variable assignment
A global variable was assigned a value. Create a variable within a type to store the desired value.
var TEST = (function (TEST) { TEST.Type = function () {

    
parseInt = function (input) { /// Number
        
return 3;
    };

    
return {};

};
return TEST; }(TEST || {}));
038 - Undefined member
An attempt was made to access a member that does not exist. Ensure you're accessing the type you expect, or add the desired member.
var TEST = (function (TEST) { TEST.Type1 = function () {

    
return {
        
number: 5
    
};

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type2 = function () {

    
var x = TEST.Type1().mumber;

    
return {};

};
return TEST; }(TEST || {}));
039 - Member assignment
An attempt was made to assign to a type member. If you must mutate an instance, create a function within the type to perform the mutation.
var TEST = (function (TEST) { TEST.Type1 = function () {

    
return {
        
number: 5
    
};

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type2 = function () {

    
var x = TEST.Type1();

    
x.number++;

    
return {};

};
return TEST; }(TEST || {}));
040 - Conflicting ternary types
A ternary operator has multiple output types. Change the two outputs to be of the same type.
var TEST = (function (TEST) { TEST.Type = function () {

    
var x = (Math.random() > 0.5) ? 7 : "8";

    
return {};

};
return TEST; }(TEST || {}));
041 - Invalid new target
The new operator was applied to an inappropriate type. Remove the new operator to call a user-defined constructor, or apply the new operator to a built-in type.
var TEST = (function (TEST) { TEST.Type1 = function () {
    
return {};
};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type2 = function () {

    
var constructor = TEST.Type1,
        
instance = new constructor();

    
return {};

};
return TEST; }(TEST || {}));
042 - Invalid argument type specification
An invalid number of arguments types were specified. Specify one type per argument in a type-specification comment.
var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
run: function (first, second) { /// Number
        
}
    };

};
return TEST; }(TEST || {}));
043 - Incorrect number of arguments
An invalid number of arguments were passed. Specify a value for every required argument, and remove any values that exceed the maximum number of arguments.
var TEST = (function (TEST) { TEST.Calculator = function () {

    
return {
        
add: function (first, second) { /// Number, Number
            
return first + second;
        }
    };

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type = function () {
    
var calculator = TEST.Calculator();

    
return {
        
getTwo: function () {
            
return calculator.add(1);
        }
    };

};
return TEST; }(TEST || {}));
044 - Conflicting argument type
An argument was specified that does not match the function argument type. Pass a value that matches the argument type.
var TEST = (function (TEST) { TEST.Calculator = function () {

    
return {
        
add: function (first, second) { /// Number, Number
            
return first + second;
        }
    };

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type = function () {
    
var calculator = TEST.Calculator();

    
return {
        
getTwo: function () {
            
return calculator.add(1, "1");
        }
    };

};
return TEST; }(TEST || {}));
045 - Invalid invocation target
A value that is not a function was invoked. Adjust the call so it is applied to a function.
var TEST = (function (TEST) { TEST.Type = function () {

    
var x = 7;

    
x();

    
return {};

};
return TEST; }(TEST || {}));
046 - Unassigned variable
A variable was declared but never assigned a value. Delete the variable, or assign it a value (and an element value, if it's a collection).
var TEST = (function (TEST) { TEST.Type = function () {

    
var value;

    
return {
        
member: value
    
};

};
return TEST; }(TEST || {}));
047 - Conflicting exception types
Multiple types of exceptions were thrown. Adjust the thrown objects to be of the same base exception type.
var TEST = (function (TEST) { TEST.Exception = function (id, message) { /// Number, String

    
return {
        
id: id,
        
message: message
    
};

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Type = function () {

    
return {
        
run: function () {
            
if (Math.random() > 0.5) {
                
throw TEST.Exception(123, "Uh oh.");
            }
else {
                
throw "Uh oh.";
            }
        }
    };

};
return TEST; }(TEST || {}));
048 - Dependency error
A circular dependency of types was encountered. Reorganize the object hierarchy so types can be inferred.
var TEST = (function (TEST) { TEST.First = function (second) { /// Second

    
return {
        
member: second.member
    
};

};
return TEST; }(TEST || {}));

var TEST = (function (TEST) { TEST.Second = function (first) { /// First

    
return {
        
member: first.member
    
};

};
return TEST; }(TEST || {}));

External Code

Not all code is lucky enough to be evaluated by JSurly, including external libraries and the built-in objects that come with the runtime environment. For JSurly to offer 100% static typing, it must know the types involved at the boundary between JSurly-inspected code and external code. These types are specified with an external-type list.

The external-type list specifies types and members. Types are specified with the type name at the beginning of a line, followed by the constructor argument types. Members of the type follow on subsequent lines, each beginning with whitespace. Members are defined using a name, followed by a type.

A special $Global$ type allows members to be added to the global namespace.

ArrayBuffer                    Number
  
length                      Number
Window
  
document                    Document
  
innerWidth                  Number
  
innerHeight                 Number
  
onresize                    Function<void>
  
setTimeout                  Function<void, Function<void>, Number>
Document
  
body                        HTMLElement
HTMLElement
  
innerText                   String
  
innerHTML                   String
  
onclick                     Function<void>
$Global$
  
window                      Window
  
encodeURIComponent          Function<String, String>
  
decodeURIComponent          Function<String, String>

JSurly comes with an external-type list that includes some type definitions for the built-in language types, web browser APIs, and the node.js framework. You can view these types by running the following command.

node jsurly.js --externs

You can also use your own custom external type lists by following the --externs option with a file path.

node jsurly.js --externs /path/to/externs /path/to/javascript

Change Log

The following lists detail the major changes in each release. Every release includes minor bug-fixes and tweaks, so they will not be mentioned in each release.

0.5

  • Added unused variable syntax.

0.4

  • Added detection of unread variables.

0.3

  • Added type template support.

0.2

  • Added support for lists of externs.

0.1

  • Initial release. Full static type checking.

Bugs

Sadly, even JSurly isn't perfect. If you find an issue, please don't hesitate to contact me.