Webstorm is [coming soon](http://joeriks.com/2012/11/20/a-first-look-at-the-typescript-support-in-webstorm-6-eap/).
## Node.js ##
standalone compiler is is available on NPM.
```
npm install -g typescript
```
To compile the TS code in this project run this in the current directory.
```
tsc -sourcemap js/_all.ts
```
## Ambient declarations ##
It is useful to have type information for the API of libraries you use.
[DefinitelyTyped](https://github.com/borisyankov/DefinitelyTyped) is a nice collection of annotations by Boris Yankov.
## Files ##
*`*.ts` are source code.
*`*.d.ts` are ambient declarations for libraries.
*`*.js` are generated by the compiler, except in `js/libs` folder.
*`*.js.map` are [source maps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/) generated by the compiler, for better debugging experience.
*`_all.ts` is convention used to enumerate file references in the project for benefit of TypeScript compiler.
If the number of files grows, you could put an `_all.ts` file into each folder, move all nested references to it and reference the nested `_all.ts` from the parent `_all.ts`.
Start reading `TodoCtrl.ts` first and continue with `Application.ts` and `Index.html`, the rest of it is easy.
AngularJS has a steeper learning curve than TypeScript.
## AngularJS ##
There is very little difference between this app and the vanilla AngularJS todo app in how AngularJS is used.
The only significant difference is that dependency injection is done via annotated constructors, which allows minification of JavaScript.
It's definitely possible to convert the vanillajs todo app into TypeScript, but TypeScript's benefits are more obvious with a *full blown framework and project structure*.
(b.nodeName||b.bind&&b.find)}functionTa(b,a,c){vard=[];m(b,function(b,g,i){d.push(a.call(c,b,g,i))});returnd}functiongc(b,a){varc=0,d;if(J(b)||F(b))returnb.length;elseif(L(b))for(dinb)(!a||b.hasOwnProperty(d))&&c++;returnc}functionza(b,a){if(b.indexOf)returnb.indexOf(a);for(varc=0;c<b.length;c++)if(a===b[c])returnc;return-1}functionUa(b,a){varc=za(b,a);c>=0&&b.splice(c,1);returna}functionV(b,a){if(oa(b)||b&&b.$evalAsync&&b.$watch)throwB("Can't copy Window or Scope");if(a){if(b===
a)throwB("Can't copy equivalent objects or arrays");if(J(b)){for(;a.length;)a.pop();for(varc=0;c<b.length;c++)a.push(V(b[c]))}elsefor(cinm(a,function(b,c){deletea[c]}),b)a[c]=V(b[c])}else(a=b)&&(J(b)?a=V(b,[]):na(b)?a=newDate(b.getTime()):L(b)&&(a=V(b,{})));returna}functionhc(b,a){vara=a||{},c;for(cinb)b.hasOwnProperty(c)&&c.substr(0,2)!=="$$"&&(a[c]=b[c]);returna}functionha(b,a){if(b===a)return!0;if(b===null||a===null)return!1;if(b!==b&&a!==a)return!0;varc=typeofb,d;if(c==typeofa&&
function(b,d){return(d?a:"")+b.toLowerCase()})}functionqa(b,a,c){if(!b)thrownewB("Argument '"+(a||"?")+"' is "+(c||"required"));returnb}functionra(b,a,c){c&&J(b)&&(b=b[b.length-1]);qa(N(b),a,"not a function, got "+(b&&typeofb=="object"?b.constructor.name||"Object":typeofb));returnb}functionlc(b){functiona(a,b,e){returna[b]||(a[b]=e())}returna(a(b,"angular",Object),"module",function(){varb={};returnfunction(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);returna(b,d,function(){functiona(c,
Q)){if(F(b)&&b.charAt(0)!="<")throwB("selectors not implemented");returnnewQ(b)}if(F(b)){vara=ca.createElement("div");a.innerHTML="<div> </div>"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}elsebb(this,b)}functioncb(b){returnb.cloneNode(!0)}functionsa(b){sb(b);for(vara=0,b=b.childNodes||[];a<b.length;a++)sa(b[a])}functiontb(b,a,c){vard=$(b,"events");$(b,"handle")&&(t(a)?m(d,function(a,c){db(b,c,a);deleted[c]}):t(c)?(db(b,a,d[a]),deleted[a]):Ua(d[a],c))}functionsb(b){vara=
function(b,c,d){a.push(d)})}),b.$inject=a}elseJ(b)?(c=b.length-1,ra(b[c],"fn"),a=b.slice(0,c)):ra(b,"fn",!0);returna}functionqb(b){functiona(a){returnfunction(b,c){if(L(b))m(b,mb(a));elsereturna(b,c)}}functionc(a,b){N(b)&&(b=l.instantiate(b));if(!b.$get)throwB("Provider "+a+" must define $get factory method.");returnj[a+f]=b}functiond(a,b){returnc(a,{$get:b})}functione(a){varb=[];m(a,function(a){if(!k.get(a))if(k.put(a,!0),F(a)){varc=ta(a);b=b.concat(e(c.requires)).concat(c._runBlocks);
try{for(vard=c._invokeQueue,c=0,f=d.length;c<f;c++){varh=d[c],g=h[0]=="$injector"?l:l.get(h[0]);g[h[1]].apply(g,h[2])}}catch(n){thrown.message&&(n.message+=" from "+a),n;}}elseif(N(a))try{b.push(l.invoke(a))}catch(i){throwi.message&&(i.message+=" from "+a),i;}elseif(J(a))try{b.push(l.invoke(a))}catch(j){throwj.message&&(j.message+=" from "+String(a[a.length-1])),j;}elsera(a,"module")});returnb}functiong(a,b){functionc(d){if(typeofd!=="string")throwB("Service name expected");if(a.hasOwnProperty(d)){if(a[d]===
returna?a.replace(/^https?\:\/\/[^\/]*/,""):a};varW={},y="",M=f.baseHref();f.cookies=function(a,b){vard,e,f,k;if(a)if(b===p)h.cookie=escape(a)+"=;path="+M+";expires=Thu, 01 Jan 1970 00:00:00 GMT";else{if(F(b))d=(h.cookie=escape(a)+"="+escape(b)+";path="+M).length+1,d>4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"),W.length>20&&c.warn("Cookie '"+a+"' possibly not set or overflowed because too many cookies were already set ("+W.length+
deleteh[a];i--}},removeAll:function(){h={};i=0;j={};l=o=null},destroy:function(){j=f=h=null;deletea[b]},info:function(){returnx({},f,{size:i})}}}vara={};b.info=function(){varb={};m(a,function(a,e){b[e]=a.info()});returnb};b.get=function(b){returna[b]};returnb}}functionyc(){this.$get=["$cacheFactory",function(b){returnb("templates")}]}functionBb(b){vara={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ";
y,m,e,ba)},y,m,e,ba)}h=null}).error(function(a,b,c,d){throwB("Failed to load template: "+d.url);});returnfunction(a,c,d,e,f){h?(h.push(c),h.push(d),h.push(e),h.push(f)):n(function(){b(o,c,d,e,f)},c,d,e,f)}}functiony(a,b){returnb.priority-a.priority}functionM(a,b,c,d){if(b)throwB("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+pa(d));}functionH(a,b){varc=h(b,!0);c&&a.push({priority:0,compile:I(function(a,b){vard=b.parent(),e=d.data("$binding")||[];e.push(c);q(d.data("$binding",
a){functionc(a){returna.indexOf(q)!=-1}functiond(){returnn+1<b.length?b.charAt(n+1):!1}functione(a){return"0"<=a&&a<="9"}functiong(a){returna==""||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"||a=="\u00a0"}functioni(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"}functionf(a){returna=="-"||a=="+"||e(a)}functionh(a,c,d){d=d||n;throwB("Lexer Error: "+a+" at column"+(v(c)?"s "+c+"-"+n+" ["+b.substring(c,d)+"]":""+d)+" in expression ["+b+"].");}functionk(){for(vara="",c=n;n<b.length;){vark=
n++;elseif(g(q)){n++;continue}else{varm=q+d(),C=Ka[q],A=Ka[m];A?(o.push({index:n,text:m,fn:A}),n+=2):C?(o.push({index:n,text:q,fn:C,json:"[,:".indexOf(s)!=-1&&c("+-")}),n+=1):h("Unexpected next character ",n,n+1)}s=q}returno}functionMc(b,a,c,d){functione(a,c){throwB("Syntax Error: Token '"+c.text+"' "+a+" at column "+(c.index+1)+" of the expression ["+b+"] starting at ["+b.substring(c.index)+"].");}functiong(){if(M.length===0)throwB("Unexpected end of expression: "+b);returnM[0]}functioni(a,
b,c,d){if(M.length>0){vare=M[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)returne}return!1}functionf(b,c,d,f){return(b=i(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),M.shift(),b):!1}functionh(a){f(a)||e("is unexpected, expecting ["+a+"]",i())}functionk(a,b){returnfunction(c,d){returna(c,d,b)}}functionj(a,b,c){returnfunction(d,e){returnb(d,e,a,c)}}functionl(){for(vara=[];;)if(M.length>0&&!i("}",")",";","]")&&a.push(v()),!f(";"))returna.length==1?a[0]:function(b,c){for(vard,
"-");)a=j(a,b.fn,s());if(b=f("<",">","<=",">="))a=j(a,b.fn,q());returna}functions(){for(vara=m(),b;b=f("*","/","%");)a=j(a,b.fn,m());returna}functionm(){vara;returnf("+")?C():(a=f("-"))?j(W,a.fn,m()):(a=f("!"))?k(a.fn,m()):C()}functionC(){vara;if(f("("))a=v(),h(")");elseif(f("["))a=A();elseif(f("{"))a=K();else{varb=f();(a=b.fn)||e("not a primary expression",b)}for(varc;b=f("(","[",".");)b.text==="("?(a=u(a,c),c=null):b.text==="["?(c=a,a=ea(a)):b.text==="."?(c=a,a=t(a)):e("IMPOSSIBLE");
returna}functionA(){vara=[];if(g().text!="]"){doa.push(H());while(f(","))}h("]");returnfunction(b,c){for(vard=[],e=0;e<a.length;e++)d.push(a[e](b,c));returnd}}functionK(){vara=[];if(g().text!="}"){do{varb=f(),b=b.string||b.text;h(":");varc=H();a.push({key:b,value:c})}while(f(","))}h("}");returnfunction(b,c){for(vard={},e=0;e<a.length;e++){varf=a[e],k=f.value(b,c);d[f.key]=k}returnd}}varW=I(0),y,M=Kc(b,d),H=function(){vara=r(),c,d;return(d=f("="))?(a.assign||e("implies assignment but ["+
b.substring(0,d.index)+"] can not be assigned to",d),c=r(),function(b,d){returna.assign(b,c(b,d),d)}):a},u=function(a,b){varc=[];if(g().text!=")"){doc.push(H());while(f(","))}h(")");returnfunction(d,e){for(varf=[],k=b?b(d,e):d,h=0;h<c.length;h++)f.push(c[h](d,e));h=a(d,e)||D;returnh.apply?h.apply(k,f):h(f[0],f[1],f[2],f[3],f[4])}},t=function(a){varb=f().text,c=Jb(b,d);returnx(function(b,d){returnc(a(b,d),d)},{assign:function(c,d,e){returnKb(a(c,e),b,d)}})},ea=function(a){varb=H();h("]");
returnx(function(c,d){vare=a(c,d),f=b(c,d),k;if(!e)returnp;if((e=e[f])&&e.then){k=e;if(!("$$v"ine))k.$$v=p,k.then(function(a){k.$$v=a});e=e.$$v}returne},{assign:function(c,d,e){returna(c,e)[b(c,e)]=d}})},v=function(){for(vara=H(),b;;)if(b=f("|"))a=j(a,b.fn,o());elsereturna};a?(H=r,u=t=ea=v=function(){e("is not valid json",{text:b,index:0})},y=C()):y=l();M.length!==0&&e("is an unexpected token",M[0]);returny}functionKb(b,a,c){for(vara=a.split("."),d=0;a.length>1;d++){vare=a.shift(),g=
10;this.digestTtl=function(a){arguments.length&&(b=a);returnb};this.$get=["$injector","$exceptionHandler","$parse",function(a,c,d){functione(){this.$id=xa();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$asyncQueue=[];this.$$listeners={}}functiong(a){if(h.$$phase)throwB(h.$$phase+" already in progress");h.$$phase=a}functioni(a,b){varc=d(a);ra(c,b);returnc}functionf(){}e.prototype={$new:function(a){if(N(a))throwB("API-CHANGE: Use $controller to instantiate controllers.");
(h=RegExp(h.substr(1,h.length-2)),e=function(a){returnk(h,a)}):e=function(a){varc=b.$eval(h);if(!c||!c.test)thrownewB("Expected "+h+" to be a RegExp but was "+c);returnk(c,a)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){varj=G(c.ngMinlength),e=function(a){return!T(a)&&a.length<j?(d.$setValidity("minlength",!1),p):(d.$setValidity("minlength",!0),a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){varl=G(c.ngMaxlength),c=function(a){return!T(a)&&a.length>l?(d.$setValidity("maxlength",
c},get:function(a){returnthis[ga(a)]},remove:function(a){varc=this[a=ga(a)];deletethis[a];returnc}};eb.prototype={push:function(a,c){vard=this[a=ga(a)];d?d.push(c):this[a]=[c]},shift:function(a){varc=this[a=ga(a)];if(c)returnc.length==1?(deletethis[a],c[0]):c.shift()},peek:function(a){if(a=this[ga(a)])returna[0]}};varrc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,sc=/,/,tc=/^\s*(_?)(\S+?)\1\s*$/,qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Db="Non-assignable model expression: ";Bb.$inject=["$provide"];
c)},"&&":function(a,c,d,e){returnd(a,c)&&e(a,c)},"||":function(a,c,d,e){returnd(a,c)||e(a,c)},"&":function(a,c,d,e){returnd(a,c)&e(a,c)},"|":function(a,c,d,e){returne(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Lc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},hb={},Yc=U.XMLHttpRequest||function(){try{returnnewActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{returnnewActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{returnnewActiveXObject("Msxml2.XMLHTTP")}catch(d){}thrownewB("This browser does not support XMLHttpRequest.");
parseFloat(e.$eval(f));returnisNaN(c)?"":(j[c]||(c=a.pluralCat(c-k)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Jd=S({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){returnfunction(a,c,i){varf=i.ngRepeat,i=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),h,k,j;if(!i)throwB("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=i[1];h=i[2];i=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!i)throwB("'item' in 'item in collection' should be identifier or (key, value) but got '"+
d.selected)z.prop("selected",u.selected=d.selected)}elsed.id===""&&q?B=q:(B=D.clone()).val(d.id).attr("selected",d.selected).text(d.label),t.push({element:B,label:d.label,id:d.id,selected:d.selected}),z?z.after(B):s.element.append(B),z=B;for(x++;t.length>x;)t.pop().element.remove()}for(;v.length>w;)v.pop()[0].element.remove()}vari;if(!(i=w.match(d)))throwB("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+w+"'.");varj=c(i[2]||i[1]),k=i[4]||