JSurly hates your JavaScript

And you'll be better off for it.

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.

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.

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.

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.

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).

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).

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.

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.

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.