Quickstart Guide
What is JSurly?
JSurly is a static analysis tool that prohibits ugly parts of JavaScript, and enforces some
clean-code conventions. In return, JSurly offers 100% static typing, and a wealth of error
detection capabilities.
What isn’t JSurly
- A new language. JSurly disallows some parts of JavaScript, 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, prohibit 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. To avoid 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 always 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 multiple 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 performs 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
|| {}));
Example Errors
Click an error to see an example containing the error, and the generated output from JSurly.
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 fortunate enough to be approved 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