Scalable class-based JavaScript for web apps and libraries.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
docs Update changelogs.txt May 27, 2014
examples Update factory.js May 5, 2014
src add exports.js Aug 9, 2014
test Update compiler.js Apr 14, 2014
LICENSE update LICENSE Aug 9, 2014
README.md update images Dec 6, 2017
core.intellisense.js Update core.intellisense.js May 2, 2014
core.js Update core.js May 27, 2014
core.min.js Update core.js May 27, 2014
package.json Update package.json May 2, 2014

README.md

jTypes: Scalable class-based JavaScript for web apps and libraries

jTypes provides developers with robust type management in JavaScript to improve the maintainability and scalability of web-based applications. By utilizing familiar and proven design patterns from popular languages such as C++, C#, and Java, jTypes can simplify the development of web apps, libraries, and tools. Since it is not a new language, jTypes doesn't require any transcompilation to messy and unmaintainable JavaScript like other web programming languages. This makes it extremely simple and straightforward, especially for developers that are experienced with classical inheritance. Using existing and upcoming language components that are implemented across all browsers and platforms, jTypes offers an efficient and effective framework for class-based object-oriented development that can quickly and easily adapt to the "quirks" of a constantly evolving web.

Coming Soon: Precompiling Class Definitions

Contents

Requirements

jTypes requires ECMAScript 5, which is supported by any modern platform or browser. This includes the following desktop and mobile browser versions:

  • Internet Explorer 9
  • Firefox 4
  • Chrome 13
  • Safari 5.1
  • Opera 11.6
  • IE Mobile 9
  • Android 3
  • iOS Safari 5
  • Opera Mobile 11.5
  • Blackberry 7

IE Mobile 9 was released with the Windows Phone 7.5 update, also known as Mango; iOS 5 was released for the iPhone 3GS, iPhone 4, iPhone 4S, iPod Touch (3rd/4th gen), iPad, and iPad 2.

Setup

Include the core.js or core.min.js file in your application:

<script type="text/javascript" src="jtypes-2.2.3.min.js"></script>

Install the jTypes module in Node.js using npm:

npm install jtypes

Enabling ECMAScript 6 features with the --harmony flag is recommended for jTypes 2.2 when using preview release 0.11.0 or higher.

IntelliSense

The core.intellisense.js file can be included in Visual Studio 2012 to add support for IntelliSense with jTypes:

jTypes IntelliSense

This file is also available in the .zip downloads on our website.

Classes

Classes help organize applications and libraries by promoting the reuse of code and facilitating the ease of maintainence. They are created by providing the jTypes compiler with a definitions object in the following format:

Class jTypes([String modifiers,] [Class base,] [Function constructor,] Object definitions)

This definitions object is a template for creating instance objects. It provides initial primitive values for fields, and function references for methods and properties. These default field values must be primitives such as booleans, numbers, strings, or symbols; otherwise the compiler will use a default value of null. The following example compiles a class Color and defines the initial primitive values of three public fields:

var Color = jTypes(
{
    'public red':   0,
    'public green': 0,
    'public blue':  0
});

var color = new Color();

console.assert(color.red   === 0, 'Color.red');
console.assert(color.green === 0, 'Color.green');
console.assert(color.blue  === 0, 'Color.blue');

color.red   = [];
color.green = new Date();
color.blue  = {};

console.assert(jTypes.isArray(color.red),         'Color.red');
console.assert(jTypes.isDate(color.green),        'Color.green');
console.assert(jTypes.isSimpleObject(color.blue), 'Color.blue');

If an instance of the Color class is instantiated, the fields will have their default primitive values. This is demonstrated by the assert() calls in the previous example. These fields can then be assigned any type of reference upon instantiation such as arrays, dates, functions, or objects.

Constructors

jTypes('Color', function(red, green, blue)
{
    this.red   = red;
    this.green = green;
    this.blue  = blue;
},
{
    // ...
});

var gray = new jTypes.Color(128, 128, 128);

Fields

jTypes('Color', function(red, green, blue)
{
    // ...
},
{
    'public readonly red':   0,
    'public readonly green': 0,
    'public readonly blue':  0
});

var gray = new jTypes.Color(128, 128, 128);

//gray.red = null; (throws)

Properties

jTypes('Color', function(red, green, blue)
{
    this._red   = red;
    this._green = green;
    this._blue  = blue;
},
{
    'private _red':   0,
    'private _green': 0,
    'private _blue':  0,
    
    'public red':   { 'get': function(){ return this._red   }, 'protected set': function(v){ this._red   = v } },
    'public green': { 'get': function(){ return this._green }, 'protected set': function(v){ this._green = v } },
    'public blue':  { 'get': function(){ return this._blue  }, 'protected set': function(v){ this._blue  = v } }
});

var gray = new jTypes.Color(128, 128, 128);

//gray.red = null; (throws)

Automatically Implemented Properties

jTypes('Color', function(red, green, blue)
{
    // ...
},
{
    'public red':   ['get', 'protected set'],
    'public green': ['get', 'protected set'],
    'public blue':  ['get', 'protected set']
});

var gray = new jTypes.Color(128, 128, 128);

//gray.red = null; (throws)

Inheritance

var AlphaColor = jTypes('sealed', Color,
{
    'public opacity': 0
});

var color = new AlphaColor();

// ...

In the next example, the Color class is compiled without referencing the return value of the compiler:

jTypes('Color',
{
    'public red':   0,
    'public green': 0,
    'public blue':  0
});

var color = new jTypes.Color();

// ...

The modifiers argument of the jTypes compiler accepts a space-separated string of keywords. However, if the final keyword in the modifiers string starts with a capital letter, it will be treated as a class name and be globally defined in the jTypes namespace.

Constraints

Type constraints restrict values to simplify the implementation and maintainence of classes. If a value is not of the respective type of the constraint, it is treated as a null reference instead:

  • array
  • Class
  • date
  • element (2.2.1)
  • error
  • function
  • jquery (2.2.1)
  • object
  • primitive
  • regexp
  • type
  • window

The type constraint object strictly checks for invalid values using a $$.type(...) that is equal to 'object', while primitive checks for invalid values using the $$.isPrimitive(...) method, and type checks for invalid values using the $$.isClass(...) method. The following example compiles a class Queue with the type constraint array on a protected field:

jTypes('Queue', function()
{
    // ...
},
{
    'protected array _queue': null,

    // ...

    'public dequeue': function()
    {
        if (this._queue)
            return this._queue.shift();
    }
});

If the protected field is checked for a null reference in the dequeue() method, it is guaranteed to have a $$.type(...) equal to 'array' and can safely call the shift() method. If a class has its name defined in the modifiers string as in the previous example, it can also be used as a constraint:

jTypes('Element', function()
{
    // ...
},
{
    'public Queue animations': null,

    // ...
});
 
var element = new jTypes.Element();
 
// ...
 
while (element.animations)
{
    var animation = element.animations.dequeue();
 
    if (!animation)
        break;
 
    // ...
}

Because the animations field is restricted using the type constraint, it is guaranteed to be of the type Queue and can safely call the dequeue() method.

Primitive Constraints

Primitive constraints use the primitive values false, 0, NaN, "", and Symbol() for default or invalid values instead of null references. Consequently, these values can be directly accessed without any reference checking because they always provide a primitive value that is neither undefined nor null:

  • boolean (bool)
  • integer (int)
  • number (float)
  • string
  • symbol

These constraints prevent values from being cross referenced because they implicitly convert wrapper objects such as new Number(...) to its primitive value. The following example compiles a struct Color using the type constraint int on the public fields:

jTypes('struct Color', function(red, green, blue)
{
    // ...
},
{
    'public int red':   0,
    'public int green': 0,
    'public int blue':  0,
 
    // ...
});
 
var green = jTypes.Color();
 
green.red   = '255';
green.green = 128.3;
green.blue  = null;
 
console.assert(green.red   === 0,   'Color.red');
console.assert(green.green === 128, 'Color.green');
console.assert(green.blue  === 0,   'Color.blue');

Since the values '255' and null do not have a typeof that is equal to 'number', the fields green.red and green.blue are assigned the default value of 0. However, green.green is assigned a truncated value because 128.3 is a primitive number.

Struct Constraints

Struct constraints use default instances for default or invalid values instead of null references. These values can also be directly accessed without any reference checking because they always provide a default instance:

  • Struct

The following example compiles the class Element using the type constraint Color on the public fields:

jTypes('Element', function()
{
    // ...
},
{
    'public Color background': null,
    'public Color foreground': null,
 
    // ...
});

var element = new jTypes.Element();

// ...
 
var background = '#' + element.background.red.toString(16) + element.background.green.toString(16) + element.background.blue.toString(16),
    foreground = '#' + element.foreground.red.toString(16) + element.foreground.green.toString(16) + element.foreground.blue.toString(16);

The values of element.background and element.foreground in the previous example can be directly accessed without any reference checking because they always provide a Color struct.

Because default and invalid values of fields and automatically implemented properties are still internally stored as null references, default struct instances are not instantiated until the value is accessed.

Operators

Nullable Operator

The ? nullable operator defaults primitive constraints and structs to null references for invalid values. It can be postfixed to the following constraints:

  • boolean? (bool?)
  • integer? (int?)
  • number? (float?)
  • string?
  • Struct?
  • symbol?

This allows primitive constraints to comply with primitive data types that may not contain a value, and structs to replicate the behavior of models. The following example compiles an abstract class Shape with the ? operator postfixed to the type constraint Color:

jTypes('abstract Shape',
{
    'protected Color? _fill':   null,
    'protected Color? _stroke': null,

    // ...
 
    'public abstract string toString': null
});

Since the protected fields default to null values, any derived implementation of the Shape class must check their values for null references. The next example compiles a class Polygon with a non-primitive type constraint array on another protected field:

jTypes('Polygon : Shape', function()
{
    // ...
},
{
    'protected array _points': null,

    // ...
 
    'public override string toString': function()
    {
        var attributes = "";
 
        if (this._points)
            attributes += 'points="' + this._points.join(' ') + '" ';
 
        // ...
 
        if (this._stroke)
            attributes += 'stroke="' + this._stroke + '" ';
 
        return '<polygon ' + attributes + '/>';
    }
});

Because the inherited field this._stroke from the previous example is nullable, it must first be checked for a null reference before being concatenated in the toString() method, which is also the case with this._points and calling the join() method.

Not-Nullable Operator

The ! not-nullable operator prevents built-in constraints with default instances from using null references. Because primitive constraints only default to null values when the nullable operator is present, the not-nullable operator can be postfixed to the following non-primitive constraints:

  • array!
  • date!
  • error!
  • function!
  • jquery! (2.2.1)
  • Model!
  • object!
  • regexp!

This operator can be postfixed to models due to the fact that models do not call the constructor unless the new operator is present. The example below compiles a class Queue with the ! operator postfixed to the type constraint array:

jTypes('Queue', function()
{
    // ...
},
{
    'protected array! _queue': null,

    // ...

    'public dequeue': function()
    {
        return this._queue.shift();
    }
});

Since the operator uses default instances for invalid values instead of null references, the dequeue() method in the previous example can safely call the shift() method without checking the protected field for a null reference.

Because default and invalid values of fields and automatically implemented properties are still internally stored as null references, default model instances are not instantiated until the value is accessed.

Coerce Operator

The ~ coerce operator performs implicit conversions for built-in types. It can be prefixed to the following constraints:

  • ~array
  • ~boolean (~bool)
  • ~date
  • ~integer (~int)
  • ~number (~float)
  • ~regexp
  • ~string

This operator converts values using global casting methods such as $$.asArray(...) or $$.asString(...). It does not alter the default value of fields and automatically implemented properties for non-primitive constraints. The following example compiles a struct Color with the ~ operator prefixed to the int type constraints:

jTypes('struct Color', function(red, green, blue)
{
    // ...
},
{
    'public ~int red':   0,
    'public ~int green': 0,
    'public ~int blue':  0,

    // ...
});

var orange = jTypes.Color();

orange.red   = '255';
orange.green = '0xA5';
orange.blue  = null;

console.assert(orange.red   === 255, 'Color.red');
console.assert(orange.green === 165, 'Color.green');
console.assert(orange.blue  === 0,   'Color.blue');

Since none of the values being assigned to the fields in the previous example are primitive numbers, the $$.asInt(...) casting method will be used to coerce the values. To alter the default value of fields and automatically implemented properties for non-primitive constraints, the ~ and ! operators can be prefixed and postfixed:

  • ~array!
  • ~date!
  • ~regexp!

Suppress Operator

at-symbol => @date

suppresses errors when jTypes.strict = true

Strict Constraints

require jTypes.strict = true, throw errors when a value is set that does not match the type of the constraint or !== null (which cannot be applied to constraints with the coerce modifier)

Namespaces

Namespaces help organize applications and libraries by controlling the scope of classes. They are created by providing the jTypes compiler with a callback function in the following format:

Object jTypes([String modifiers,] [Array dependencies,] Function callback(jTypes))

This callback is invoked in the context of a namespace object. Using the provided argument, calls to the compiler will also be within the scope of the namespace.

If a modifiers string is not provided, the callback is invoked in the context of the global namespace.

In the following example, a struct Color is compiled in the scope of the System.Drawing namespace:

jTypes('namespace System.Drawing', function($$)
{
    $$('struct Color', function(red, green, blue)
    {
        // ...
    },
    {
        // ...
    });
 
    var gray = new this.Color(128, 128, 128);
});

The namespace modifier in the modifiers string is optional and can be provided for readability.

Since this callback is invoked in the context of the namespace, the struct can be instantiated using this.Color in the scope of the callback function. Other scopes can instantiate this struct using the fully qualified reference from the global namespace:

var white = new jTypes.System.Drawing.Color(255, 255, 255);

These scopes also allow base classes and type constraints to be resolved from either the current or parent namespaces without requiring a fully qualified name. In the next example, an abstract class Shape is compiled in the scope of the System.Drawing.Drawing2D namespace:

jTypes('namespace System.Drawing.Drawing2D', function($$)
{
    $$('abstract Shape',
    {
        'protected Color _fill':   null,
        'protected Color _stroke': null,
 
        // ...
    });
});

The type constraint Color on the fields of the Shape class can be resolved without using the fully qualified name because the struct is defined in a parent namespace. If a class Rectangle is compiled in the same namespace as outlined in the example below, it can also reference the Shape class without using the fully qualified name:

jTypes('namespace System.Drawing.Drawing2D',
[
    // ...
],
function($$)
{
    $$('Rectangle : Shape', function()
    {
        // ...
    },
    {
        // ...
    });
});

While the Rectangle class is compiled in a separate callback function with its own unique scope and dependencies, the Shape class can still be referenced because it is already defined in the System.Drawing.Drawing2D namespace.

Dependencies

If a base class or type constraint cannot be resolved from either the current or parent namespaces, a dependencies array can prevent the need to specify a namespace or fully qualified name. The following example compiles an abstract class Control in the System.Forms namespace using an array of dependency strings:

jTypes('namespace System.Forms',
[
    'using System.Drawing',
    'using System.Drawing.Drawing2D',

    // ...
],
function($$)
{
    $$('abstract Control',
    {
        // ...
    });
});

This array of dependency strings can contain two types of declarations; alias definitions and namespace includes. Alias definitions contain the = assignment operator, while namespace includes do not.

Includes

Namespace includes allow identifiers in a namespace to be resolved without having to specify the namespace. The example below includes the System.Drawing namespace in the scope of the callback function:

jTypes('namespace System.Forms',
[
    'using System.Drawing',
 
    // ...
],
function($$)
{
    $$('Textbox : Control', function()
    {
        // ...
    },
    {
        'public virtual Color background': ['get', 'set'],
        'public virtual Color border':     ['get', 'set'],
 
        // ...
    });
});

Since the System.Drawing namespace is included in the scope, the type constraint Color on the automatically implemented properties can be resolved without using Drawing.Color or System.Drawing.Color.

If a base class or type constraint is ambiguous between namespace includes, then it must be uniquely qualified.

Aliases

Alias definitions make it easier to qualify an identifier for a class or namespace. The next example creates the alias Color for the fully qualified name System.Drawing.Color:

jTypes('namespace System.Forms',
[
    'using Color = System.Drawing.Color',
 
    // ...
],
function($$)
{
    $$('Textbox : Control', function()
    {
        // ...
    },
    {
        'public virtual Color background': ['get', 'set'],
        'public virtual Color border':     ['get', 'set'],
 
        // ...
    });
});

Because this alias is defined in the scope of the callback function, the type constraint Color on the automatically implemented properties can be resolved. The fully qualified name on the right side of the alias definition can also be Drawing.Color due to the fact that System.Forms is a child namespace of System.

Aliases cannot hide a class already defined in the current namespace.

When there are conflicting names, aliases can be referenced directly using the :: operator. The following example uses the namespace alias qualifier to directly reference an alias in the type constraints on the automatically implemented properties:

jTypes('namespace System.Forms',
[
    'using Drawing = global::System.Drawing',
 
    // ...
],
function($$)
{
    $$('Textbox : global::System.Forms.Control', function()
    {
        // ...
    },
    {
        'public virtual Drawing::Color background': ['get', 'set'],
        'public virtual Drawing::Color border':     ['get', 'set'],
 
        // ...
    });
});

The right side of an alias definition must be a namespace to use it with the :: operator. The global:: namespace alias qualifier is reserved for the global namespace. This allows base classes and type constraints to be referenced using globally qualified names.

Class Modifiers

abstract

abstract classes: cannot be instantiated and can have abstract methods and properties

expando

expando classes: inject the __self object into the root of prototype chain of the instance matrix (and it is only available in legacy mode)

internal

internal classes: hide their type from the type() method on all instances and the __type accessor on the public instance (and non-internal classes cannot inherit from internal classes)

model

models: have optional constructors (new operator invokes constructor), default instances (without new operator)

optimized

optimized classes: dynamically create instance objects (and it is not available in legacy mode)

primitive

primitive classes: have primitive type constraints (for fields and automatically implemented properties), clone() and equals(obj) methods

sealed

sealed classes: cannot be inherited

struct

structs: have optional constructors (new operator invokes constructor), default instances (without new operator), no inheritance, clone() and equals(obj) methods

unlocked

unlocked classes: have chainable __base instances

Class Member Modifiers

abstract

abstract members: must be overridden in a derived class (and can only be used on methods or properties)

const

const members: are not [Writable] and can only be used with the prototype or static modifiers

hidden

hidden members: are not [Enumerable] (fields and properties are enumerable by default, methods are not)

new

new members: hide an inherited member (when not overriding)

override

override members: override an inherited abstract or virtual member

private

private members: are only accessible in the methods and properties of the class they are defined in

protected

protected members: are only accessible in the methods and properties of the class they are defined in and the methods and properties of any derived classes

prototype

prototype members: are defined on the prototype of the class constructor (Class.prototype)

public

public members: are accessible everywhere for the class they are defined in and any derived classes as well

readonly

readonly members: are only writable inside the constructor (and can only be used with fields and automatically implemented properties)

sealed

sealed members: cannot be overridden and must be used with the override modifier

static

static members: are defined on the class constructor (Class)

virtual

virtual members: can be overridden in a derived class (and can only be used on methods or properties)

visible

visible members: are [Enumerable] (methods are not enumerable by default, fields and properties are)

Global Settings

jT_FunctionLock

default: false

jT_Harmony

default: true

jT_Legacy

default: false

jT_PrototypeLock

default: false

jT_Shorthand

default: true

jT_Storage

default: false

jT_Writable

default: false

Performance

jTypes 2.2 Performance

Futures

  • Generics
  • Interfaces
  • Events (Prioritized Queues)
    • DOM Event Binders
  • Dynamic Namespaces
  • Internal Methods and Accessors
  • Custom Constraint Handlers
  • Argument Type Constraints (Methods)
  • Factories
  • Indexers *
  • Partial Classes
  • Observable Classes *
  • Reflection (Introspection)

* Requires ECMAScript 6 Proxies

Precompiling (fusion Integration)

jTypes will soon provide support for precompiling static class definitions using our fusion language framework currently under development. The lexer-parser utilities of fusion will be used to build an abstract syntax tree for each static class definition in a .jt file and transpile them into a .js file. Any classes with inline strings, functions, and object literals will be transpiled into a jTypes class that requires no compiling at runtime. This will provide developers with the ability to utilize dynamic runtime class definitions for rapid prototyping and precompiled static class definitions for efficiency. It not only offers the best of both worlds, but allows your class definitions to be interchangeable between the two.

https://github.com/gaulinsoft/fusion

The fusion language superset can also be combined with jTypes in .fjt files to allow for inline HTML and CSS in class definitions.

Media

jTypes