Ramblings of a .NET Developer
Showing posts with label TypeScript. Show all posts
Showing posts with label TypeScript. Show all posts

5 March 2015

Thursday, March 05, 2015 | by Paul | Categories: | No comments
Finally, after two days trying to figure out why TypeScript was not building a load of TypeScript files in Visual Studio I've tracked and fixed the problem...sort of...

Two days ago...

So, I need to make some changes to web applications developed by another team. All good, I'll just get it from TFS, build it and go from there. Yes, I know, TFS is a bag of spanners, but you've got to work with what you have sometimes.

Anyway, got latest and build the solution. Only to find, that for every TypeScript project (unlucky 13 in this case) failed to build. Looking at the error, each project was coming up with a useless generic error message stating that the build "exited with code 1". Yeah, thanks for the detail on that.

Anyhow, looking at the build output wasn't helping too much either.
So, I checked my machine to make sure it was using the same version as the other devs, which it was.
All good there as well.

For the rest of the day, I went through the process of googling, checking more settings on my machine, fiddling with project settings and generally going around in circles.

Yesterday

I found that there was an MSBUILD configuration which was specifying to use a local copy of TypeScript instead of the installed version. The only reason I can think of was to do with server build configurations.

Anyway, after looking at this I didn't see any glaring issues. After more googling and getting nowhere fast I un-installed all versions of TypeScript, and re-installed each in turn.
Still no joy.
So, next was to un-installed Visual Studio 2005 Preview (which didn't do a good job) and re-installed Visual Studio 2013. Rebooted a few times, cleaned up my Path environment variable which TypeScript manage to forget about.

After all this, it was still stuffed!!  :(

Today

So, today I went back to basics.
Can I compile a JavaScript file from the command line. Yes
Does the same file build through Visual Studio. No

What's the difference?
Looking at one of the csproj files it is using a Microsoft.TypeScript.targets build file.

Opening this up in Visual Studio reveals a build config file.
I happened to re-build the solution again and found a slightly different error. msb6006 tsc.exe
Looking at the build output identified a problem at line 96.


 <Target Name="PreComputeCompileTypeScript">  
   <VsTsc  
    ToolPath="$(TscToolPath)"  
    ToolExe="$(TscToolExe)"  
    Configurations="$(TypeScriptBuildConfigurations) COMPUTE_PATHS_ONLY"  
    FullPathsToFiles="@(TypeScriptCompile)"  
    YieldDuringToolExecution="$(TscYieldDuringToolExecution)"  
    OutFile="$(TypeScriptOutFile)"  
    OutDir="$(TypeScriptOutDir)"  
    ProjectDir="$(ProjectDir)"  
    >  

Line 96 is the opening VsTsc tag. Now I'm getting somewhere.
So, it looks like one of the variables is incorrect.

  <PropertyGroup>  
   <TypeScriptToolsVersion Condition="'$(TypeScriptToolsVersion)'==''">1.0</TypeScriptToolsVersion>  
   <!--<TscToolPath Condition="'$(TscToolPath)' == ''">$(MSBuildProgramFiles32)\Microsoft SDKs\TypeScript</TscToolPath>-->  
   <TscToolPath Condition="'$(TscToolPath)' == ''">$(SharedBinariesTypeScriptToolPath)</TscToolPath>  
   <TscToolPath Condition="'$(TypeScriptToolsVersion)' != ''">$(TscToolPath)\$(TypeScriptToolsVersion)</TscToolPath>  
   <TscToolExe Condition="'$(TscToolExe)' == ''">tsc.exe</TscToolExe>  
   <TscYieldDuringToolExecution Condition="'$(TscYieldDuringToolExecution)' == ''">true</TscYieldDuringToolExecution>  
  </PropertyGroup>  

Everything looked good, and was resolving correctly. But I decided to uncomment the TscToolPath that was pointing to my installed SDK and ignore the local one. I also hardcoded the version.
Guess what? It worked!!!

So, there is some issue with the version of TypeScript being used in the one used by the solution.

The weird bit...

So, I'm writing this blog entry to explain the steps and went back to the version of Microsoft.TypeScript.targets that was not working so I can show the original settings. Re-compiled the solution to get the exact error messages and it still built!!!

I've not done this yet, but the TypeScriptToolsVersion element is not recognized by Visual Studio, which is hinting to maybe an incorrect version of the TypeScript Add-In or something on those lines.

But, I don't care at the moment. It's Friday, and got it all working before the weekend!

22 February 2015

Sunday, February 22, 2015 | by Paul | Categories: , , , | No comments
So, Nathan introduced me to his TypeScript Tiny-IOC implementation last week. As a .NET dev, using IoC/DI is standard practice and nothing new and was immediately comfortable with the concept.

This library has good intentions, but I think it goes against what TypeScript is all about, which is to have type checking and provide more OO like functionality.
In this type of implementation as each Interface has to be doubled up I feel it will cause unnecessary code. Also, you will need to keep each of the IInterfaceChecker implementations up to date whenever it's referring class changes, resulting in a maintenance nightmare.

The problem is that TypeScript does not generate objects for Interfaces. And that is fair enough, as TypeScript in my view is a developer aid for those who prefer the OO world. At the end of the day, it still generates JavaScript.

My main problem with it is the existence of IInterfaceChecker. This "interface" requires the implementing object to provide the methods they implement and the type name. This to me is an immediate "code smell", so I set about seeing how it could be done without magic strings. Also, I'm still fairly new to the web world, so I thought it would be a good excuse to have a play. :)

So, in JavaScript world there is no such thing as an interface, so my first point is to not use it in TypeScript, instead create a Class named with I as the prefix for anything we want to act like an interface. Specifying I as a prefix is a convention that has been used quite widely so I hope it would not be too much of a problem.

So, to the code...

Models

So, lets create an IPerson class that has some basic details. Remember, this is a Class and not an Interface so we get the generated JavaScript.
 class IPerson {  
      firstName: string;  
      lastName: string;  
      salary: number;  
        
      constructor(){  
           this.firstName = '';  
           this.lastName = '';  
           this.salary = 1;  
      }  
   
      fullName() {  
         return this.firstName + ' ' + this.lastName;       
      }  
 }  

Next, I created some example implementations. One correct implementation, and another 3 that are in some way invalid.

Our Person class is a true implementation of IPerson
 class Person {  
      firstName: string;  
      lastName: string;  
      salary: number;  
        
      constructor(){  
           this.firstName = '';  
           this.lastName = '';  
           this.salary = 1;  
      }  
   
      fullName() {  
         return this.firstName + ' ' + this.lastName;       
      }  
 }  

Below are 3 other objects. The first does not implement salary. In the second, salary is a string and not a number and the third, the fullname function takes two arguments, whilst the interface does not specify any.
 class PersonWithNoSalary {  
      firstName: string;  
      lastName: string;  
        
      constructor(){  
           this.firstName = '';  
           this.lastName = '';  
      }  
   
      fullName() {  
         return this.firstName + ' ' + this.lastName;       
      }  
 }

class PersonWithInvalidSalary {  
      firstName: string;  
      lastName: string;  
      salary: string;  
        
      constructor(){  
           this.firstName = '';  
           this.lastName = '';  
      }  
   
      fullName() {  
         return this.firstName + ' ' + this.lastName;       
      }  
 }

class PersonWithInvalidFullName {  
      firstName: string;  
      lastName: string;  
      salary: number;  
        
      constructor(){  
           this.firstName = '';  
           this.lastName = '';  
      }  
   
      fullName(firstname: string, lastname: string) {  
         return firstname + ' ' + lastname;       
      }  
 }

JavaScript

For those that don't have TypeScript, below is the equivalent JavaScript for the models.
 var IPerson = (function() {  
   function IPerson() {  
     this.firstName = '';  
     this.lastName = ''  
     this.salary = 1;  
     this.fullName = function(){  
       return this.firstName + ' ' + this.lastName;  
     }  
   }  
   
   return IPerson;  
 })();  
   
 var Person = (function() {  
   function Person() {  
     this.firstName = 'Paul';  
     this.lastName = 'Sanders';  
     this.salary = 100000;  
     this.fullName = function(){  
       return this.firstName + ' ' + this.lastName;  
     }  
   }  
     
   return Person;  
 })();       
   
 var PersonWithNoSalary = (function() {  
   function PersonWithNoSalary() {  
     this.firstName = 'Paul';  
     this.lastName = 'Sanders';  
     this.fullName = function(){  
       return this.firstName + ' ' + this.lastName;  
     }  
   }  
     
   return PersonWithNoSalary;  
 })();       
   
 var PersonWithInvalidSalary = (function() {  
   function PersonWithInvalidSalary() {  
     this.firstName = 'Paul';  
     this.lastName = 'Sanders';  
     this.salary = 'My Salary';  
     this.fullName = function(){  
       return this.firstName + ' ' + this.lastName;  
     }  
   }  
     
   return PersonWithInvalidSalary;  
 })();       
   
 var PersonWithInvalidFullName = (function() {  
   function PersonWithInvalidFullName() {  
     this.firstName = 'Paul';  
     this.lastName = 'Sanders';  
     this.salary = 100000;  
     this.fullName = function(firstname, lastname){  
       return firstname + ' ' + lastname;  
     }  
   }  
     
   return PersonWithInvalidFullName;  
 })();  

Reflection

Now that I have these, I set about creating a Reflection class to help me validate the implementation or one object, against another. After a bit of playing, googling and borrowing a little code from StackOverflow I came up with the following.
 var Reflection = (function () {  
   function Reflection () {  
     this.getArgs = function(fn) {  
       var args = fn.toString ().match (/^\s*function\s+(?:\w*\s*)?\((.*?)\)/);  
       args = args ? (args[1] ? args[1].trim ().split (/\s*,\s*/) : []) : null;  
       return args;  
     };  
     this.implements = function(obj,interface) {       
       var self = this;  
       for(var item in interface)  
       {  
         if(typeof interface[item] == 'function' && typeof obj[item] != 'function')  
         {  
           return false;  
         }  
         else if(typeof interface[item] == 'function' && typeof obj[item] == 'function')  
         {  
           var interfaceArgs = self.getArgs(interface[item]);  
           var objArgs = self.getArgs(obj[item]);  
           //check arguments  
           if(interfaceArgs.length != objArgs.length)  
           {  
             return false;  
           }  
           //as js has no types, can only check names  
           for(i=0;i < interfaceArgs.length;i++)  
           {  
             if(interfaceArgs[i] != objArgs[i])  
             {  
               return false;  
             }  
           }  
         }  
         else if(!obj.hasOwnProperty(item))  
         {  
           return false;  
         }  
         else if(typeof interface[item] != typeof obj[item])  
         {  
           return false;  
         }  
       }  
       return true;  
     }  
     this.dump = function(obj, elementId) {  
       var d = document.getElementById(elementId);  
       var properties = '';  
       var methods = '';  
       var classType = obj.constructor.name;  
       for(var item in obj)  
       {  
         if(typeof obj[item] == 'function')  
           methods += (item + '(' + this.getArgs(obj[item]) + '), ');  
         else  
           properties += (item + ' : ' + typeof obj[item] + ', ');  
       }  
       d.innerHTML=d.innerHTML + "<p>Type: " + classType + "<br>Properties: " + properties.substr(0,properties.length-2) + '<br>Methods: ' + methods.substr(0,methods.length-2) + '</p>';  
     };
   }  
   return Reflection;  
 })();  

The getArgs function was used from a thread on StackOverflow, which is a nice and simple method to obtain arguments of a function.

The implements function is the main routine. It finds each property and function on the interface and checks the implementing object to see if has the same property/function and that their types match.

The dump function is a helper function for me quickly visualize an object properties into an element.

IoC

As I'm still learning JavaScript etc, it is only natural that I wrote my own IoC container. :)
This is a pretty simple one, and does not implement DI.
After a bit more playing, this is what I came up with.
 var IoC = (function () {  
   function IoC () {  
     this.__keys__ = new Array();  
     this.__values__ = new Array();  
   
     this.isRegistered = function (interface) {  
       var t = typeof this.__keys__[interface];  
       var index = this.__keys__.indexOf(interface.__proto__);  
         
       if (index == -1) {  
         return false;  
       }  
   
       return true;      
     }  
       
     this.register = function(obj,interface) {  
       if(!this.isRegistered(interface))  
       {  
         var r = new Reflection();  
         if(r.implements(obj, interface))  
         {  
           this.__keys__.push(interface.__proto__);  
           this.__values__.push(obj);  
         }  
         else  
         {  
           throw "Object '" + obj.constructor.name + "' must implement '" + interface.constructor.name + "'";  
         }  
       }  
     }  
   
     this.resolve = function(interface) {  
       if(this.isRegistered(interface)) {  
         var index = this.__keys__.indexOf(interface.__proto__);  
         var template = this.__values__[index];  
         var obj = Object.create(template);  
           
         return obj;  
       }  
       else  
       {  
         throw "No registration found for '" + interface.constructor.name + "'";  
       }  
     }  
       
     this.unRegister = function(interface) {  
       if(this.isRegistered(interface))  
       {  
         var index = this.__keys__.indexOf(interface.__proto__);  
         this.__keys__.splice(index,1);  
         this.__values__.splice(index,1);  
       }  
     }  
   }  
   return IoC;  
 })();  

The functions are pretty self explanatory, but the main thing to note is that I'm storing the prototype of the "interface" object as my key. In the register function I'm using the Reflection object to valid the implementation. If the object does not implement the interface, then I throw an exception.

So, to test all this I could use some testing framework like Jasmine. But, I've not used this yet, and didn't want to get bogged down in figuring it out. So, I've created a regular html page to do this for me. Maybe a Jasmine implementation should be the next item on my long TODO list...

Again, here is the page to test the functionality.
 <!DOCTYPE html>  
 <html>  
 <head>  
   <script src="models.js"></script>  
   <script src="reflector.js"></script>  
   <script src="ioc.js"></script>  
 </head>  
 <script language="javascript">  
   
   function validateImplementations() {  
     var i = new IPerson();  
     var validPerson = new Person();  
     var invalid1 = new PersonWithInvalidFullName();  
     var invalid2 = new PersonWithInvalidSalary();  
     var invalid3 = new PersonWithNoSalary();  
       
     var r = new Reflection();  
   
     var result1 = r.implements(validPerson, i);  
     var result2 = r.implements(invalid1, i);  
     var result3 = r.implements(invalid2, i);  
     var result4 = r.implements(invalid3, i);  
   
     r.dump(i,"data");  
     var e = document.getElementById("data");  
     e.innerHTML += "<hr />";  
       
     r.dump(validManager,"data");  
     printResult('data', 'Does Person implement MyInterface?', result1);  
   
     r.dump(invalid1,"data");  
     printResult('data', 'Does PersonWithInvalidFullName implement MyInterface?', result2);  
       
     r.dump(invalid2,"data");  
     printResult('data', 'Does PersonWithInvalidSalary implement MyInterface?', result3);  
   
     r.dump(invalid3,"data");  
     printResult('data', 'Does PersonWithNoSalary implement MyInterface?', result4);  
   }  
   
   function printResult(id,question, result) {  
     var e = document.getElementById(id);  
     var text = '<p style="font-style: italic"><b>' + question + '&nbsp;' + (result ? '<span style="color: green">Yes</span>':'<span style="color: red">No</span>');  
     text += '</b></p><hr />';  
       
     e.innerHTML+=text;  
   }  
     
   function testIoc() {  
     var ioc = new IoC();  
       
     var i = new IPerson();  
     var personTemplate = new Person();  
       
     ioc.register(personTemplate, i);  
     var text = "";  
       
     if(ioc.isRegistered(i))  
       text += ("Registered '" + personTemplate.constructor.name + "' as implementation for " + i.constructor.name);  
     else  
       text += ('Could not register ' + personTemplate.constructor.name + ' as implementation for ' + i.constructor.name);  
       
     var person1 = ioc.resolve(i);  
     text += ("<br>Resolved Person 1 (" + person1.constructor.name + ")");  
     manager1.firstName = 'Fred';  
     manager1.lastName = 'Flintstone';      
     text += ("<br>Person 1 Name: " + person1.fullName());  
       
     var person2 = ioc.resolve(i);  
     text += ("<br><br>Resolved Person 2 (" + person2.constructor.name + ")");  
     manager2.firstName = 'Wilma';  
     manager2.lastName = 'Flintstone';  
     text += ("<br>Person 2 Name: " + person2.fullName() + "<br>");  
   
     ioc.unRegister(new IPerson());  
     if(ioc.isRegistered(i))  
       text += ("<br>Attempt to unregister " + i.constructor.name + " failed");  
     else  
       text += ("<br>Unregistered " + i.constructor.name);  
             
     try  
     {  
       var person3 = ioc.resolve(i);  
     }  
     catch(err)  
     {  
       text += ("<br>Attempt to resolve failed: " + err);  
     }  
       
     var e = document.getElementById("iocdata");  
       
     try  
     {  
       var otherPerson = new PersonWithInvalidSalary();  
       ioc.register(otherPerson, i);  
       text += "<br><br>Invalid person correctly registered! :(";  
     }  
     catch(err)  
     {  
       text += ("<br><br>Attempt to register invalid person failed:<br><div style='color: red'>ERROR : " + err + "</div>");  
     }  
   
     e.innerHTML = text;  
   }  
 </script>  
 <body>  
   <button onClick="testIoc()">Test Ioc</button>  
   <div id="iocdata"></div>  
   <hr />  
   <button onClick="validateImplementations()">Validate Implementations</button>  
   <div id="data"></div>  
 </body>  
 </html>  
   

As you can see, I've saved the models, reflection and ioc in separate JavaScript files. And yes, the IoC would probably benefit from require.js, again something I've not used yet.

An example of everything is here.