Typecast.js


Typecast.js - Typing for Javascript

In a sentence, Typecast solves all the simple problems, so you can focus on the big ones.

Typecast fixes what's wrong with Javascript by creating a complete foundation for variable typing in Javascript. It works both in a browser and on Node.js servers. Typecast pairs well with jQuery and does not modify any native javascript methods. Typecast does not require a custom compiler like CoffeeScript, TypeScript or Clojure.

Typecast is a replacement to underscore.js. Underscore provides 60 helper functions for 13k, while for only 10k more, Typecast provides over 200 additional helper functions. Typecast speeds development by reducing the length and variety of code required to complete common programming tasks, reducing both bugs and troubleshooting time.

Download

 

Typecast v0.2 Commented Source (17k)

Typecast v0.2 Compressed Source (10k)

Typecast v0.2 + Underscore.js v1.4 Compressed (24k)

Typecast v0.2 + Underscore.js v1.4 + JSON for IE7 support (27k)


Type can run most anywhere

AMD/require module, Node.js module, or just include it in the Browser

Type requires underscore.js and JSON.

var type = require("typecast");

type as a function works out the "typeof" problems in Javascript

here is the full list of possible return values from type()

type()			// "undefined"
type(null)		// "null"
type(NaN)		// "nan"
type(true)		// "boolean"
type(123)		// "number"
type("123")		// "string"
type([])		// "array"
type(arguments)	// "arguments"
type({})		// "object"

The name of each type is also a function

each type's function asks if the value "is" of that type

type.str("123")		// true
type.obj(null)		// false
type.fn(_)			// true

The New Natives

Typecast extends javascript typing to provide a wide base of type detections.

base

  • null (nul)
  • undefined (undef)
  • NaN (nan)
  • defined (def)

dual

  • boolean (bool)
  • true (tru)
  • truthy (truy)
  • false (fal)
  • falsey (faly)

numeric

  • number (num)
  • integer (int)
  • floating (flt)
  • vector (vct)

characters

  • string (str)
  • plain string (plain)
  • email (email)
  • unique id (unique)

collections

  • collection (col)
  • object (obj)
  • array (arr)
  • arguments (args)
  • geoposition (geo)
  • json (json)

functional

  • function (fn)
  • queue (que)

queries

  • regex (reg)

time

  • date (date)

^ back to top


Type is a Foundation

Type allows you an easy way to alway know certian things are true. This makes your code more robust in a wider set of browsers and environments.

each type library has a(), to(), can()

type.num.a() 			// 0, an example value
type.num.to("123x")		// 123 
type.num.to("xyz") 		// 0
type.num.can("123x")	// true
type.num.can("xyz")		// false

Over 200 Helper functions for only 10k

Most of them are copies of other functions, but are organized by the type of value they operate on. This way you don't have to remember or know that _.indexOf is safer than the native Array.indexOf or _.isNaN is safer than the native isNaN(). Type always uses the most cross-compitable version of each helper.

type.num.min === Math.min
type.arr.indexOf === _.indexOf // not Array.indexOf
type.nan == _.isNaN // not the native isNaN

Booleans

  • not = returns the opposite

Integer

  • dec = return --v
  • inc = return ++v

Floating Point Number

  • eq = fixed point comparison
  • decimal = return just the decimal portion

Arguments

  • toArray = Array.prototype.slice.call
  • sub = Array.slice
  • pre = Array.unshift

Date

  • shortMonth = return a month abbreviation
  • shortDay = return a day abbreviation
  • day = return a full day name

Json

  • stringify = JSON.stringify
  • parse = JSON.parse

Numbers

  • random = _.random
  • range = _.range
  • round = Math.round
  • abs = Math.abs
  • ceil = Math.ceil
  • floor = Math.floor
  • min = Math.min
  • max = Math.max
  • exp = Math.exp
  • sqrt = Math.sqrt
  • pow = Math.pow
  • log = Math.log
  • sin = Math.sin
  • cos = Math.cos
  • tan = Math.tan
  • asin = Math.asin
  • acos = Math.acos
  • atan = Math.atan
  • plus = return a + b
  • minus = return a - b
  • mod = return a % b
  • times = return a * b
  • div = return a / b
  • sign = return the sign of a number
  • between = is value between high and low
  • eq = return a === b
  • neq = return a !== b
  • lt = return a < b
  • lte = return a <= b
  • gt = return a > b
  • gte = return a >= b
  • fixed = Number.toFixed
  • hex = Number.toString(16)
  • binary = Number.toString(2)

String

  • fromCharCode = String.fromCharCode
  • charAt = String.charAt
  • charCodeAt = String.charCodeAt
  • length = String.length
  • upper = String.toUpperCase
  • lower = String.toLowerCase
  • escape = _.escape
  • unescape = _.unescape
  • template = _.template
  • contains = _.contains
  • array = _.toArray
  • ltrim = left trim
  • rtrim = right trim
  • trim = left and right trim
  • truncate = truncate to length

Functions

  • noop = empty function
  • compose = _.compose
  • identity = _.identity
  • bind = _.bind
  • memorize = _.memorize
  • delay = _.delay
  • defer = _.defer
  • throttle = _.throttle
  • debounce = _.debounce
  • once = _.once
  • after = _.after
  • wrap = _.wrap
  • times = _.times
  • always = always return a value
  • sample = run at a sample rate

Collections (Arrays and Objects)

  • each = _.each
  • map = _.map
  • reduce = _.reduce
  • reduceRight = _.reduceRight
  • find = _.find
  • filter = _.filter
  • where = _.where
  • reject = _.reject
  • contains = _.contains
  • invoke = _.invoke
  • pluck = _.pluck
  • sortBy = _.sortBy
  • groupBy = _.groupBy
  • countBy = _.countBy
  • shuffle = _.shuffle
  • toArray = _.toArray
  • length = _.length
  • all = _.all
  • any = _.any
  • max = _.max
  • min = _.min
  • mapValue = map for object values

Arrays

  • push = Array.push
  • pop = Array.pop
  • shift = Array.shift
  • unshift = Array.unshift
  • slice = Array.slice
  • splice = Array.splice
  • sort = Array.sort
  • range = _.range
  • toObject = _.toObject
  • zip = _.zip
  • indexOf = _.indexOf
  • lastIndexOf = _.lastIndexOf
  • first = _.first
  • last = _.last
  • initial = _.initial
  • rest = _.rest
  • compact = _.compact
  • flatten = _.flatten
  • without = _.without
  • union = _.union
  • intersection = _.intersection
  • difference = _.difference
  • unique = _.unique
  • sortedIndex = _.sortedIndex
  • average = returns average of all values

Objects

  • extend = _.extend
  • clone = _.clone
  • pairs = _.pairs
  • invert = _.invert
  • functions = _.functions
  • pick = _.pick
  • omit = _.omit
  • defaults = _.defaults
  • tap = _.tap
  • keys = _.keys
  • values = _.values
  • isEqual = _.isEqual
  • isEmpty = _.isEmpty
  • isElement = _.isElement
  • has = _.has
  • result = _.result
  • isSubobject = is one object inside another

Type is Extensible

Create your own types, share them with your friends!

type.extend({
	name: "positive",
	a : 1,
	is: function(){},
	to: function(){},
	can: function(){},
	inherits: ['num'], // optional
	methods: { // optional
		special: function(){}
	} 
});

or add methods to an existing type

type.extend({
	name: "json",
	methods: {
		special: function(){} // clobers any existing method named "special"
	} 
});

^ back to top


What's Wrong with Javascript?

The main value statement of Typecast is that it "speeds development by reducing the length and variety of code required to complete common programming tasks, reducing both bugs and troubleshooting time." Typecast smooths out many of the eccentricities of Javascript and how Javascript is implemented across browsers. Below are a few examples of how Typecast does this.

Strong and Loose

Typecast doesn't remove access to the native prototypes, and does this without trapping you into a custom compiler that could quickly fall out of date. Converting CoffeeScript back to Javascript requires a complete re-write. Typecast on the other hand, enables you to use strongly typed variables while keeping the code base in native Javascript syntax, regardless of the environment.

What is Truth?

Frequently, it is convenient to use Javascript shorthand to detect for the existence of a variable:

if (someVar) { 
	// do stuff with someVar
}

var newVar = (oldVar) ? oldVar : defaultValue;

If someVar is undefined or null then these statements work, but if the value of the variable has a value of 0 then these statements evaluate false even though the value is defined. If this is the behavior you want, then great, but each of these shorthand statements are potential bugs that may not show up until a test case where the variable exists, but doesn't have a truthy value. Typecast solves this by differentiating True from Truthy and False from Falsey.

type.fal(0);	// false
type.fal(0);	// true
type.fal('');	// false
type.faly('');	// true
type.fal([]);	// false
type.faly([]);	// true

0 == "0"	// true
true == "true"	// false ?!?

type.num.to(0) == type.num.to("0")	// true
type.bool.to(true) == type.bool.to("true")	// true

What is a Number?

One problem with numbers in Javascript is that they are not particularly easy to parse becuae there are so many types of them. Typecast aims to parse all of the conventional number patterns in Javascript. These include:

  • conventional decimal numbers: 0 1 5 137 1.3
  • decimal numbers in exponential form: 6.67e-11 -1.127e20
  • octal numbers, for example: 077 -01234 0312
  • hexadecimal numbers, for example: 0xFF -0xCCFF 0xabcdef
  • predefined mathematical constants (properties of the Math object)

Another problem that numbers have is that floating point comparison is way off. See this StackOverflow article.

0.1 + 0.2 == 0.3 		// returns false
0.1 + 0.2 				// returns 0.30000000000000004 !?!

"" + 1 + 2;				// "12"
"" + ( 1 + 2 );			// "3"
"" + 0.0000001;			// "1e-7"
parseInt( 0.0000001 );	// 1 !?!

Or how about what isn't a Number. Not a Number, or NaN, is the only Number which is not equal to itself. Making it an intersting puzzle, an oddity, but not very practical as a return value for Number casting.

NaN == NaN				// false ?!?
typeof NaN === 'number'	// true, 'Not A Number' is a Number ?!
type.nan(NaN) 		// true
type.num(NaN)		// false

Number('abc')			// NaN
parseInt('abc')			// NaN
parseFloat('abc')		// NaN
type.num.to('abc')		// 0

type.nan(NaN);		// true
type.nan(1);		// false
type.num(NaN);		// false
type.num(1);		// true

What is an Array?

Most Javascript programmers are aware of the inconsistancies in Javascript's native typeof and instanceof operators. For example:

typeof {} 				// object
typeof [] 				// object, not array ?!?
typeof null				// object ?!?
[] instanceof Array 	// true
[] instanceof Object 	// true ?!?
{} instanceof Object 	// true again
[] == []				// false !!

Because of this, the typeof operator can be used on most value types, except array. Rather than trying to remember which type comparison should be preformed on each value type, Typecast provides Type.is() as a complete interface for all value types. Type.object.is() works the same way as jQuery.isPlainObject() only detecting for objects that are not any of Javascript's 'special' objects like arrays, null, functions, or Infinity.

type.obj({})	// true
type.obj([])	// false
type.arr([])	// true
type.arr({})	// false