Preface
In recent years I had to deal more and more with JavaScript as a web programmer. JavaScript was always part of the job, but it required more and more attention since it became popular to create single-page web applications which are all build on top of Ajax. The same time the number of libraries which tried to circumvent the design flaws of the JavaScript language grew. There are also attempts to establisch a better language for client-side programming in web applications. Unfortunately they all failed.As a programmer who relies on the object oriented programming paradigm, I have at least an ambivalent relationship to JavaScript. That programming language has 5 times more desing flaws than instructions. Even well established JavaScript libraries, which claim for themselfe to be professionel, look more like a result of a hacker contest than professionel code. Even worse, the language itself evolves with the speed of continental plates. There is no hope that the language will reach a mature state in the next 5000 years. Those which are responsible for the language specification have also no intention to get rid of the ages old errors. Offically because they won't brake backward compatibility.
Who ever came up with that lame excuse shall burn in hell for eternity.
But then came Anders Hejlsberg, my personal hero and inventor of TypeScript.
He showed us a way out of the misery and created a language which looks and feels almost like a real OO programming language. You do not longer have to deal with all the crap and hacks and well known errors to keep your JavaScript programs running. Now you can program your script code just like you always did in C#. You also get full IntelliSense and error messages at compile time. At least when you use visual studio for your programming task.
But how could Anders Hejlsberg establish a new cliend-side script language when all others (even google and god) failed. The simple answer is:
"Because he didn't even try."
TypeScript code compiles ( or transpiles, if you like ) to JavaScript. This way your browser won't even notice that you are already a lightyear ahead of the lame script engine restriction. Your browser will only see JavaScript code. This way he was able to introduce a new language without the need to create a runtime environment for it.
Someday, somebody should investigate why one of the worst programming languages man invented (JavaScript), was the only thing the browser vendors could agree on, during the dark ages of the browser war in the last millenium.
Anders Hejlsberg did a great job and gave us lambda expressions, inheritance, something like a type system, typed function arguments, access modifiers and much much more. All that stuff you always wanted to have in JavaScript but didn't dare to ask for. Since the language is all new, he is still focused on developing the language itself and the compiler. So he didn't have time to create a full blown ecosystem around that language, like you may know from "Phyton" or "Node.js". You can use existing javasript libraries in TypeScript with the help of TypeScript definition files ("d.ts"). Those "d.ts" files work much like header files in C or C++. They introduce the javscript library functions to TypeScript so that you can use them from within your TypeScript code. That gives you a headstart when you want to use TypeScript for serious coding, but don't have the time to wirte all necessary libraries by yourself. But you shouldn't settle for this. With TypeScript you have the tool to write much more robust JavaScript code than ever before. My vision is, that there will come a time when those libraries are written in typescript in the first place. That will give us more robust libraries, code which is much easier to understand and you get also rid of the "d.ts" files. Event those which are not enlightened can still use your code, because after compiling it is plain JavaScript for them. That's the reason why I started this project.
Since I'm a great fan of LINQ in C#, it was a natural choice for me to start with a LINQ library. There are also some modules which are not LINQ specific at all, but build the foundation of a robust and eloquent coding style. Namely that are the type info functions in the "TS.Utils" module and the exception classes.
After all, I must say it is fun to write code in TypeScript. I had already lost my hope and didn't belive that there will one day fun in JavaScript programming. But the great visionary and master of superb programming languages disabused me. I take a deep bow in front of him and I hope you will have as much fun in using my library as I have in writing it.
Introduction
This library is mainly a reprogrammed version of the default C# LINQ library in TypeScript. The library may grow over time, but at the time of this writing the development is focused on the LINQ extensions.Below is a link to the description of the standard query operators as they are defined by microsoft:
http://msdn.microsoft.com/en-us/library/bb397896.aspx
There are other libraries available which offer the same functionality as this one. They may even be faster or offer more extensions than this one. But as far as I can tell, they were designed with different design goals in mind. Even if the design goals are not explicitly named, you can tell hat by looking at their code.
First of all, those libraries which I know are all written in plain JavaScript. But I would like to see more libraries and programs written in TypeScript in the first place. So I started my own project.
Second, I like a "Clean Code" coding style. Unfortunately most JavaScript programmers don't bother for a clean coding style. They prefer the "Hacker Style" which makes it always a nighmare if it comes to a situation where you have to debug through their code.
Third, I always vote for robustness over speed. All my functions implement an expensive parameter checking and fail gracefulley by throwing meaningfull exceptions.
To make my point clear. There are JavaScript libraries which run much faster and there are libraries which offer a lot more functionality. But if you need a robust and well designed library your are at the right place.
All my public classes and functions got a code comment which shows up in the editor during typing. At leas if you use visual studio for development. That feature makes it a lot more easier to write code in JavaScript then you might expect. Check it out and you will never want to miss it.
Even with that design goals in mind and extensive testing, there might be errors in this library. For that reason I would like to hear from you when you find one. Please drop a note at "lord.saumagen@gmail.com" with a full description of the error.
Since this is a work in progress, you will find modules, classes or functions which are not supposed to be used in business code, because of their immature state. Those functions which pass their unit tests are considered to be stable. Run the test suite in the "Test" directory to get a better understanding of which of the library functions are stable and can be used.
The Modules
Encoding
All classes, modules and interfaces which belong to the 'TS.Encoding' namespace can be found in the corresponding subdirectory called 'Encoding'.At the time of this writing, there are 2 encoding classes available. One is the 'Base64' encoding class and the other is the 'HTML' encoding class.
The Base64 class offers two static methods. One is called 'encode' the other is called 'decode'. The encode functions takes a data string and returns a base64 encoded string which holds the base64 encoded data.
The 'decode' function reverses the process. The decode function takes a base64 encoded string and returns the original data string.
The HTML class offers two static methods. One is called 'encode' the other is called 'decode'. The encode funstion takes a string as input and returns a result string where every character of the original string is substitute by its HTML Unicode character reference.
The 'decode' function reverses the process. The decode function takes an arbitrary string and substiutes every detected Unicode character reference by its corresponding character.
Exception
The 'TS.Exception' class and some derived classes can be found in the subdirectory called 'Exception' in file 'Exception.ts'.The 'TS.Exception' class is a substitute for the poor JavaScript 'Error' classes which are part of the language specification in JavaScript. So your are forced to use one of those classes to signal an exceptions, even if none of them is appropriate. The 'TS.Exception' class is an attempt to introduce the power and flexibility of the exception system use in C#, into TypeScript.
Since TypeScript builds on JavaScript, and JavaScript doesn't offer a type system, it's also impossible to determine a specific exeption type. To circumvent that problem you should follow a design convention when creating your own exception classes derived from 'TS.Exception' or it's subclasses.
Before I will explain the convention, let's have a look at the definition of the 'TS.Exception' class.
export class Exception implements Error { private _message: string; private _innerException: Exception; public get innerException(): Exception { return this._innerException; } /** * @implemnts * Error.message */ public get message(): string { return this._message; } /** * @implemnts * Error.name */ public get name(): string { return this.type; } public get type(): string { return "TS.Exception"; } /** * @constructs * TS.Exception */ constructor(message?: string, innerException?: Exception) { this._message = (message) ? message : ""; this._innerException = (innerException) ? innerException : null; } /** * @overwrite * Object.toString */ public toString(): string { return this.type + ((this.message.length > 0) ? " :: " + this.message : ""); } }//END classAs you can see, the 'TS.Exception' class implements the 'Error' interface. The interface is defined as follows.
interface Error { name: string; message: string; }That means that the 'TS.Exception' class and all of it's descendants have a 'name' and a 'message' property. The properties are implemented as read only properties. They can only be set once in the constructor. The class also implements a constant 'type' property which holds the fully qualified type name of the current class as string. The class overwrites the 'toString' methode inherrited from the class 'Object'. This method returns a string wich consists of the type name as defined in property 'type' and if available the message as defined in property 'message' separated by the following string: " :: ".
There is nothing special with this class, except that it gives you the freedom to create exception classes for your own purpose by deriving from 'TS.Exception' or one of it's derivates. There are only two rules you should follow.
1. Overwrite the inherrited 'type' property with the fully qualified name of your derived class.
2. Call the constructor of the base class in the constructor of your derived class.
When you open the 'Exception.ts' file, you will notice that there is more than one exception class defined in this file. The next in order of occurence is the 'ArgumentException' class. You can see the definition of that file below. As you can see, the 'type' property overwrites the inherrited 'type' property and the constructor of the base class is called in the constructor.
export class ArgumentException extends Exception { private _argumentName: string; private _argumentValue: any; /** * @overwrite */ public get type(): string { return "TS.ArgumentException"; } get argumentName(): string { return this._argumentName; } get argumentValue(): any { return this._argumentValue; } /** * @constructs */ constructor(argumentName: string, argumentValue: any, message?: string, innerException?: Exception) { super(message, innerException); this._argumentName = (argumentName) ? argumentName : ""; this._argumentValue = argumentValue; } }//END classNow you may ask yourself what's the advantage of this construction at all, because you could also use an 'Error' class or one of the six other errors defined in the JavaScript language definition. Of course you can. But if those classes doesn't fit your needs, you will have to create your own exception system anyway. So you can also stick with this system from the beginning and don't have to bother with the poor implementation in JavaScript.
It makes is also easier to determine the type fo an exception. Lets say you wrote an exception handler but you are only interested in those exceptions you are able to handel in your handler. Your code might look like this.
try { //Any expression or statement that might throw en exception. }//END try catch (ex) { switch ((<TS.Exception> ex).type) { case "TS.ArgumentNullException": { //Handle the 'ArgumentNullException'. } case "TS.IndexOutOfRangeException": { //Handle the 'IndexOutOfRangeException'. } default: { //Rethrow the exception if not handled. throw ex; } }//END switch }//END catchYou see, this way it is pretty easy to filter exceptions by it's type. You don't have to parse the message string to determine the exception type. Even if the message is empty, you still can tell what kind of exceptions you got by looking at the 'type' attribute.
If you want to see a working example of specialised exceptions classes an how they are used, take a look into the file 'Extensions.ts' in the 'Linq' subdirectory. You will find the exceptions classes which are specific to the 'TS.Linq.Extension' module and have no meaning outsider of that context.
LINQ
All classes, modules and interfaces which belong to the 'TS.Linq' namespace can be found in the corresponding subdirectory called 'Linq'.The extension functions are all defined in the 'TS.Linq.Extensions' module. The corresponding source file is the 'Extensions.ts' file in the 'Linq' directory. The module functions resemble the public static functions defined in C#.
Most of the extension functions are also bound to the 'TS.Linq.Enumerable' class which can be found in file 'Enumerable.ts'. This way you can use the fluent interface for the LINQ extensions which you may already know from C#.
The table below shows all default LINQ extensions as defined by Microsoft and theire execution behavior. Those wich are marked green are considered complete implemented and tested.
Those wich are marked yellow are not yet implemented or not tested.
Those which are marked read will never be implemented. Largely because of the missing type system in JavaScript.
The default LINQ extension functions listed by Microsoft.
Implemented LINQ extensions. | ||
---|---|---|
Name | Execution behavior | Overloads |
aggregate | Immediate execution | 1 overload |
all | Immediate execution | |
any | Immediate execution | 1 overload |
asEnumerable | NOT IMPLEMENTED! See: 'fromArray'. | |
average | Immediate execution | |
cast | NOT IMPLEMENTED! See: 'toArray'. | |
concat | Deferred execution | |
contains | Immediate execution | 1 overload |
count | Immediate execution | 1 overload |
defaultIfEmpty | Deferred execution | |
distinct | Deferred execution | 1 overload |
elementAt | Immediate execution | |
elementAtOrDefault | Immediate execution | |
empty | Immediate execution | |
except | Deferred execution | 1 overload |
first | Immediate execution | 1 overload |
firstOrDefault | Immediate execution | 1 overload |
groupBy | Deferred execution | 3 overloads |
groupJoin | Deferred execution | 1 overload |
intersect | Deferred execution | 1 overload |
join | Deferred execution | |
last | Immediate execution | 1 overload |
lastOrDefault | Immediate execution | 1 overload |
max | Immediate execution | |
min | Immediate execution | |
ofType | NOT IMPLEMENTED! | |
orderBy | Deferred execution | 1 overload |
orderByDescending | Deferred execution | 1 overload |
range | Immediate execution | |
repeat | Immediate execution | |
reverse | Deferred execution | |
select | Deferred execution | |
selectMany | Deferred execution | |
sequenceEqual | Immediate execution | 1 overload |
single | Immediate execution | 1 overload |
singleOrDefault | Immediate execution | 1 overload |
skip | Deferred execution | |
skipWhile | Deferred execution | |
sum | Immediate execution | |
take | Deferred execution | |
takeWhile | Deferred execution | |
thenBy | Deferred execution | |
thenByDescending | Deferred execution | |
toArray | Immediate execution | |
toDictionary | NOT IMPLEMENTED! | |
toList | NOT IMPLEMENTED! | |
toLookup | NOT IMPLEMENTED! | |
union | Deferred execution | |
where | Deferred execution |
There are also some extensions which are no standard query operators. The extension function "fromArray" is essential for the use of the "TS.Linq" module. The others are useful for test scenarios. The "cycle" and "random" extension functions are no query operators at all. They are generators and should only be used in conjuntion with a following "take" operator to limit their output. Otherwise you will run into a memory leak.
Additional extensions | ||
---|---|---|
Name | Execution behavior | |
cycle | Deferred execution | |
forEach | Deferred execution | |
fromArray | Immediate execution | |
random | Deferred execution | |
shuffle | Deferred execution |
How to use
If you want to use the extension functions you have to create an instance of "Enumerable<T>" in the first place. You get one by casting an array of any type into an enumerable by calling the function "fromArray" which is defined in the "TS.Linq.Extensions" module. Since there are no other collection types available in JavaScript, there is also no other type you could cast to an enumerable. Below is an excample which shows how to call that function.newEnumerable = TS.Linq.Extensions.fromArray([anyArray]);
That function is also assigned to the "Enumerable<T>" class as a static function, you can call that function from that class or any instance of that class too.
newEnumerable1 = TS.Linq.Enumerable.fromArray([anyArray]);
newEnumerable2 = newEnumerable1.fromArray([anyArray]);
Once you have an enumerable, you can use the LINQ extension functions to work on that enumerable. You can either call the static extension functions which require an enumerable as argument, or you can use the fluent interface on the enumerable instance.
Here is an example which calls a static function:
_testResultCarEnumerable = TS.Linq.Extensions.distinct(_testInputCarEnumerable, (first, second) => first.name == second.name);
The same call using the fluent interface:
_testResultCarEnumerable = _testInputCarEnumerable.distinct((first, second) => first.name == second.name);
If you are familiar with LINQ in C#, there is nothing new to you. The function above retuns an enumerable as a result to the call of "distinct". You can go on with that enumerable and use other LINQ extension functions on it, or you can call the "toArray" function wich casts the result into a JavaScript array of the result type. From that moment on, you can work with the result array as you would with any other JavaScript array.
So that's all to say about this library. If you are curious about the deffered execution behavior or if you are new to LINQ at all you should read about it in one of the online tutorials or at MSDN. There is also a vast amount of books which cover this topic.
Security
All classes, modules and interfaces which belong to the 'TS.Security' namespace can be found in the corresponding subdirectory called 'Security'.The class implements the 'SHA1' some of the hash algorithm as described by the NIST in document 'fips-180-4.pdf'. The table below shows which algorithms are implemented yet.
Implemented hash algorithms | ||
---|---|---|
Name | Description | |
SHA1 | fips-180-4.pdf | 6.1 SHA-1 | |
SHA224 | fips-180-4.pdf | 6.3 SHA-224 | |
SHA256 | fips-180-4.pdf | 6.2 SHA-256 |
Utils
All classes, modules and interfaces which belong to the 'TS.Utils' namespace can be found in the corresponding subdirectory called 'Utils'.The 'Utils' module hosts all the functions and classes which have a general purpose but no common problem domain. They do very different things but have in common that they come in handy at mundane programming tasks.
You will find the following functions, enums and classes at the 'TS.Utils' module level.
Classes: | |
---|---|
TS.Utils.TypeInfo
|
A static class which offers a bunch of functions for type detection of javascript objects. |
Enums: | |
TS.Utils.TypeEnum
|
An enumeration which lists all detectable JavaScript types. |
Functions: | |
compactArray(arr: Array<any>): Array<any>
|
The functions takes a sparse array of any type and returns a compacted array of the same type. |
createGUID(): string
|
Creates a version 4 random GUID and returns it as string in it's canonical representation. |
padLeft(source: string, fillChar: string, length: number): string
|
The functions takes a string and a fill character and returns a string which is filled up with the fill character from the left, until the total number of characters matches the number specified in length. |
HTMLCollectionToArray(collection): Array<HTMLElement>
|
The functions takes a HTML collection and returns that collection as an array of HTMLElements. |
normalizePath(path: string): string
|
The functions takes a path string and returnes a string where every occurence of a '\' is replaced by a '/'. |
Test
You will find the unit tests fore each module in the subdirectory 'Test'. All modules have a corresponding TypeScript file in this directory. Some have also a corresponding file written in plain JavaScript. The naming rule for the unit tests is as follows:"TS_" + [Fully Qualified Module Name] + "_test.js" for a test file wirtten in TypeScript or,
"TS_" + [Fully Qualified Module Name] + "_test_plain.js" for a test fiel written in plain JavaScript.
The namespace / module separator in the fully qualified module name is the '_'.
There is also a HTML page for each module following the same naming convention. That HTML page is used to start the unit tests in your browser. I chose the QUnit testing framework for this project. Running a unit test is only a matter of opening the corresponding HTML page in your Browser.
There is also a HTML page called "TestSuite.html" which will run all unit test at once.
The subdirectory "Data" in the "Test" directory holds the test data for the unit tests.
The test files in plain JavaScript need a little explanation. Some tests regarding the error handling of the modules wasn't realizeable in TypeScript. The simple reason for that is, TypeScript doesn't allow you to write as erroneous code as you can write in plain JavaScript. So I had to write those tests in plain JavaScript.
License
This software is licensed under the Open Software License version 3.0.You will find the full licens text in file OSL_V3.0.html in the Documentation directory of this project.
© lord.saumagen@gmail.com, 2014
Version
The current version of this module collection is 1.0.3. Beginning with the version 1.0.1 the TypeScript compiler in version 1.3.0.0 is used. That compiler came with the "TypeScript 1.3 for Visual Studio 2013" set up. The module collection will not be backward compatible, because it will make use of the new language features. If you are not sure which version you are currently using, you can proof by running the "tsc.exe" file with the "-v" flag as shown below.