jtypes

2.2.3 • Public • Published

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.

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 using the jTypes CDN:

<script type="text/javascript" src="http://cdn.jtypes.com/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. 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!

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.

Contribute

We strongly encourage anyone who is interested in contributing to contact us through any of the various social mediums on our website (www.jTypes.com).

jTypes is an open-source library developed by Gaulinsoft, a small software consulting company in Chicago, IL. It was created internally in our free-time to simplify the development of some of our other frameworks and libraries which we also hope to release in the near future. Therefore, any contributions are greatly appreciated and really go a long way to help us out.

We hope you find jTypes to be a very useful tool in the development of your applications or libraries and we have many more great things to come, so be sure to check-in every once in a while to see what's new!

Versions

Current Tags

  • Version
    Downloads (Last 7 Days)
    • Tag
  • 2.2.3
    0
    • latest

Version History

  • Version
    Downloads (Last 7 Days)
    • Published
  • 2.2.3
    0
  • 2.2.2
    0
  • 2.2.1
    0
  • 2.2.0
    0
  • 2.1.7
    0

Package Sidebar

Install

npm i jtypes

Weekly Downloads

0

Version

2.2.3

License

Apache-2.0

Last publish

Collaborators

  • redline