It is fantastic - so glad you posted. On the transpilation thing; Babel still has a total use case. TypeScript doesn't provide es6 APIs such as Map / Promise / Array.find etc I output es6 from my TypeScript and pipe that into Babel to get the new APIs and to do transpilation. Works great. (There's a number of ways to do it; I use WebPack with ts-loader and babel-loader which rocks.)
You're absolutely right. Flow seems to be a very good alternative.
One thing I would like to add regarding the .js files, TypeScript 1.8 added a flag --allowJs which makes some sanity checks to the plain old .js files.
It sounds like --allowJs alleviates the need to create TypeScript definitions for everything? So if you have an existing project based entirely on JavaScript, you can gradually convert each file to .ts without having to do anything else?
Well, you still need some transpiling in order to remove the Flow type signatures. In practice, they end up being pretty similar in terms of needing a build step.
Been using typescript since its introduction. It really helped me improve my JavaScript code quality and most importantly it's really good at keeping large projects tidy and easy to comprehend.
It would be interesting to hear more about your experiences. I have been quite hesitant to use more advanced TypeScript features and stayed on a type system related annotations.
WebStorm does offer syntax highlighting of HTML inline templates, but I'm sure that's a matter of time before that feature comes to VS Code. I do think that WS does provide a nice workflow at the expense of a much heavier tool.
Coding/language support is very comparable between WebStorm and VS Code. WebStorm is a much larger and full-featured IDE, though. Most people won't use most features, but I found that I missed one or two things in VS Code and had to go back to WebStorm.
I'm still debating between the two. I do miss the code corrections (like the suggestions to add module imports) when using VS Code. That said, I'm very impressed with the progress that VS Code has made in such a short time.
About the text editor: I think all editors use the same TypeScript compiler APIs. That would mean that you would get pretty much same suggestions, error lists, etc. Correct me if I am wrong. VS Code could have some other API calls or project file support.
JS Jabber episode was great! Full of great insights.
I use TypeScript from both Emacs and VSCode, depending on my mood. They do both use the same underlying compiler API, but the VSCode support is more pleasant in how it's surfaced in the editor: ctl-click on a variable to jump to its definition, f2 to rename the variable under the cursor, or ctl-T to open a query field to jump to a symbol.
Emacs is capable of all of these things (because Emacs is capable of anything) but it doesn't work that way out of the box nor is there an already-written package around that makes that easy. To rename you type M-x tide-rename-symbol and it's inconvenient enough that I forget to use it.
Ashamedly at one point I was on the anti-Angular 2 bandwagon due to Typescript, because Microsoft. When I started learning Angular 2 myself I've found I actually enjoyed working with Typescript, for many reasons. (confidence that my code wasn't a ball of mystery before it ran, using inline templates, succinctness of annotations, etc)
At the same time, you're gonna need to use some preexisting libraries, and run into the issue of not having types available. (though they do exist for most mainstream libs) However, I found creating types for any given lib pretty flexible, and that you can usually get away with just enough types.
Ha, I'm on the anti-Angular2 bandwagon due to Angular1. I use React and Typescript and love it. Curious if you've tried React. Or Angular1
(TBH Angular1 was pretty great at the time, but after React I look back at those monstrous template files, ng-everything, plugins and workarounds, and only the bad memories surface).
The real decision re: NG2 v React is more about opinionated frameworks v non-opinionated frameworks. If you're by yourself and/or have tight control over all of your libraries, React is fine. If you're a bigger operation and need to trust the restrictions of the framework, NG2 is possibly a better choice.
Angular blew a loooooot of my trust in Google, who in the past always put out the highest quality stuff. I built an entire app in the damn thing only to realize as it got bigger that it wouldn't scale. Sure you can use less HTML templates, but that's the whole point so that it's easier for designers to approach / fix.
Sure I should have checked it out but the documentation didn't exactly highlight it (at the time anyway), and with the services, controllers, testability, etc it just looked like they'd thought it all through.
Frankly after finding React there's no way I'm giving NG2 even a look in.
That's a little harsh. It was an MVVM framework along the same lines as backbone, ember 1.0, knockout, and pretty much every other framework of the day, plus a lot of extra functionality. It's not really google's fault that MVVM has limitations.
Two of the main problems I had with angular - the template syntax and the way the digest cycle worked, both contributed to its scaling factor.
The template syntax meant you would always wonder what a parameter meant when passed to a directive. Essentially, all parameters passed to a directive were strings, but angular would parse some strings to mean "the property of a parent scope", and some more convoluted variations of that(check out https://docs.angularjs.org/api/ng/service/$compile#-scope-). Furthermore, using the recommended ng-controller directive created great spaghetti code, with template blobs where the current scope is almost impossible to track. Applying multiple directives to the same element sometimes required special parameter parsing, which further made the situation confusing. This confusion meant the more stuff you had, the more time you had to spend carefully memorizing it all so you knew what was going on.
The digest cycle fundamentally limits angular's scale. It was designed with some scale in mind at first - a digest cycle triggered at a specific scope would only run watchers under that scope, thus limiting the effect. I assume they hit the problem that there was no way to define explicitly "global" watchers, or to tell another scope to update itself. This issue apparently crept into the development of angular and local digests were pretty much abandoned in favor of using scope.$apply(), which triggers a digest cycle at the topmost scope. Local digests were not really talked about in the documentation, which was littered with examples of scope.$apply(), all of angular's provided directives used scope.$apply() and pretty much no one wrote any code limiting the amount of watchers triggered in an update. React specifically addressed this point with shouldComponentUpdate and the ecosystem built around it, which is one of its biggest performance benefits.
Further criticism would be the terminology confusion. Angular talked about directives, modules, controllers, filters, services, factories, providers, etc.
In essence, providers/factories/services were all just singletons instantiated in different ways(providers being the progenitor and factories/services being variations).
Filters were poorly named, as they were actually mapping functions or value formatters. Thus came the infamous "filter filter", which filtered elements in a collection, as opposed to say the "currency filter", which added currency markers to numbers. They were also incredibly inefficient, running on every digest cycle, even after the attempted fixes with "stateless filters"(http://www.bennadel.com/blog/2766-stateless-filters-don-t-ap...)
Controllers were essentially stunted directives, provided via the ng-controller directive. They were paraded as the way to start "simple" development, but led to a lot of bikeshedding about where to use controllers and where directives, the confusion around directives requiring controllers and the limitations around that, how to appropriately bind directive parameters to a controller's "scope"(not an actual scope but the controller function object itself), the bindToController function resolving that problem(introduced in 1.4?), etc, etc.
Modules were also a weird concept without much practical use. They do not provide a separate namespace(all your angular elements go into the same one), they don't leverage angular tooling to import your code(you have to import your code manually e.g. add script tags). Mostly they provided the config() and run() functions which would let you sched...
Great answer, I feel the same way. Let's not forget the documentation, way too abstract. Probably the result of an overengineered framework.
Sadly I'm stuck having to deal with it, and I can honestly say I hate it and it makes me want to cry. Might have to do with the way it was used, though. I find myself having to write a directive each time I have to populate a fucking select2.
Last week was my first week trying out Angular2 + TypeScript. I use mostly Java & C# at my day job. I was actually a really shocked at how fast i was able to gain a working knowledge of TypeScript. The only thing i don't like is my project has extra files everywhere (.js + .ts). However there is probably a way to make my IDE hide them or something.
WebStorm does a nice job of this. It'll compile on the fly for you, and it collapses the compiled files as child files under the .ts files. So by default you'll only see source files, but can expand and see output if you really need to.
If you use --outDir, wouldn't the .js files that import the transpiled .ts files need to point to outDir instead?
This is the kind of stuff that makes the "incremental" adoption argument hard for me to swallow. If I can't rewrite a .js file to .ts without any further ripple effects within the project, then it's not truly incremental.
I wrestled with this stuff last week and ultimately ended up going with flow since it gave me type checking _and_ incremental adoption, without complicating my existing babel/webpack stack.
Hey, I work on the TypeScript team. I hope you'll reconsider, and maybe you can fill me in on the issues you ran into.
Your .js files should typically be relative to each other, so unless you're using absolute paths (which you usually shouldn't!), this hasn't been a problem for other users. Is there something that I'm missing?
Hey Dan, thanks for responding. I think the key point you may be missing (or perhaps I missed a flag somewhere) is that I want relative requires but I don't want the intermediate js and sourcemap files cluttering up my project (and assuming I have an existing babel/webpack stack I'm happy with -- I just want the type checking).
I want the ability to convert any existing js file within the project to ts without having to touch a single other source file. I also want the type checker to assume that if I don't have a type definition for a package, that I don't want the use of that package to be type checked (without being forced to rewrite my ES6 imports to commonjs).
I was able to do this with flow but not typescript.
Hey rhymohr, I think I see what you mean. If you're already using something like Babel & Webpack, it should just be a matter of using a TypeScript loader like ts-loader[1] or awesome-typescript-loader[2]. TypeScript should just fit into that build step.
Hey Rob any idea why VSCode gives this warning on export class for Components?
>[ts] Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.
I prefer frameworks that focus on pure javascript because the ones that don't tend to allow you too use javascript but have sparse documentation on how to use it effectively, instead focusing on typescript. When I first tried Angular 2 their javascript "hello world" example wouldn't even work, it was some bug in their site. I already need to use all sorts of other tools and abstraction layers when building websites/apps, typescript feels like just another abstraction layer (which it is), that means another vim plugin for typescript (or a bloated IDE), another npm dev dependency, another layer to the build process, so I try to avoid it for these reasons.
I don't really work in the Javascript ecosystem, so when learning Angular 2 recently, I found all the other tools to carry far more cognitive load than Typescript.
The great irony of this (and fwiw, I completely agree, and I work on the angular core team...) is that by embracing platform features in ng2 we've opened up a mess of other issues to learn.
A good example of this is modules. Angular 1 had its own module syntax (partly because modules weren't really a thing when it started). You could write scripts, concat them together and go.
In angular2 we embraced ES6 modules, which means a developer has to deal with loading and bundling etc.
One nice side effect here though is we can leverage more tooling from the rest of the JS community (eg, the great work of webpack) rather than having angular-specific solutions.
3rd party libraries / frameworks written with non-JavaScript languages is worth another blog post and discussion. I agree completely with you that libraries / -most- frameworks should be written in plain old JS.
The reason is simple, contributing and reading the project source code should not start by learning a programming language. It is a different scenario when a team in a company has decided to learn and write their project with TypeScript/CoffeeScript/etc. language.
About tooling, I think writing modern JavaScript (ES2015, ES2016) is beneficial and therefore, you need a tool like Babel. When you go to that path, why not use TypeScript instead and get ES2015/ES2016 + type system + great compiler?
(this won't be quite true after TS 2.0, but..) TypeScript doesn't support everything ES6 or even everything Babel does, and some of the stuff it does support are not matching the standard, in subtle ways. Then you're missing out on the Babel plugin ecosystem. You can use TS and Babel together for SOME of that (but not all of it, syntax parser and all).
It may still be a slam dunk to you, but it's not as "obvious" an answer as you make it sound.
With the arrival of TS 2.0 this month (hopefully) we'll have some cool features:
a union types [1]
b type guards to work with them [1]
c nonullable types [2]
As a Scala developer I like Typescript a lot. Which is a first for me in browser development.
But I'm not so fond of webpack et al. So I created a sbt-typescript plugin. And now I have incremental compilation of NG2 - Play apps for both the browser side and the server side. In a single build definition. It's a nice experience.
I am in the same boat as the poster. The main reasons I use typescript instead of scala.js are the vast amount of definition files already made (the scala.js translator of these leaves a lot to be desired), and the fact that it's closer to the es6 community which makes maintenance, contributions, and adoption much more likely. So basically because you stay closer to the JS world.
The only downside is lack of "isomorphic" code reuse which I gladly accept anyways because I rarely see server and client side code reuse as beneficial beyond simple models and validation.
As codablah said: the availability of type definitions is a major difference in ease of use.
The other reason is that I can add a specialised frontend dev to the team and reasonably expect them to be able to work in Typescript.
I think the biggest advantage is that typescript emits code that is good JavaScript. This means if I decide to stop using typescript, I can just use the generated JavaScript. It also means that there transpiled file is smaller. Using scala.js for a small project wouldn't make sense if you cared about file size at all. It could be worth it more as your project grows, but your then you are that much more commited. There's no exit strategy like with typescript.
I'm always curious where that came from. TypeScript does not emit good JS. It's pretty garbage and not reasonably usable by humans. TS supports ES6 target, where it more or less just strips TS-specific features, but that's only useful if you target a very small subset of browsers and limit what you use.
Even "6to5" (previous name for Babel), which originally had as a design goal that it would emit good JS (and back then, it did!) quickly lost that when they tried to aim for spec correctness.
TypeScript actually punts on the standard in some cases (eg: TypeScript classes are not ES6 compliant) to generate more readable code, but it's still not "good" code and I certainly would never consider working with that.
I remember hearing the same argument in the early 80s for the benefit of C vs Assembly. And it actually was a good argument. A lot of people, myself included, learned "real programming" (ie assembly) by using C as training wheels. But a funny thing happened on the way to the forum - C became real programming and nobody remembered or learned assembly - myself included. Then the same thing happened ten years later with the transition from C to C++.
It did, and it will. My point is that just like with these "legacy" languages, the next generation of TypeScript developers are no more likely to "drop down into JavaScript" as a C developer is to "drop down into assembly". So our development tools and debuggers must be really solid at the language level that we code in. And just like I skipped the "transpiling" phase of C++, I'll probably skip the transpiling phase of TypeScript, and wait for the tooling and runtime to be more mature.
The advantage is it gives you an escape route if Typescript ends up dying. I worked on a project that had to do a giant dead language -> Java conversion, and it was extremely painful.
I was just about to complain how TS fails totally for typechecking backbone models or immutablejs datastructures: both situations where obj.get("foo") and obj.get("bar") return a particular type but there's no way of having TS handle that except defining them as any. But it turns out string literal types in 1.8 will make that work. And this was out since February! I should reevaluate TS for my omniscient project.
The example you give has been supported since day 1. Example: document.createElement("canvas") returns HTMLCanvasElement. A limited form of string literal types has always been supported specifically for overloading return types. The newer support added in 1.8 is a way more powerful generalisation.
It could also benefit from constant propagation (iirc the term) where you could write obj.get(Item.name) and both get type check and stay refactoring friendly (though with cost of some more typing).
The two things coming in 2.0 that most excite me are
- moving types to npm (despite the great work of Blake on typings, managing type definitions is still out of band) - npm install @types/somelib will be great.
- AST transforms, which will open up some interesting options for bundling and optimizations, sort of like babel's plugin ecosystem.
I don't know about moving all of the current typings to beneath an NPM org, but TypeScript can look for the `typings` field in a package's package.json file.
An NPM package will be typed and you won't have to download typings separately. It may be improper to publish typings with your code instead of letting the user install them separately, but at that point it ceases being clean.
In this case, my `toxiproxy-node-client` package is written in TypeScript and the typings are published alongside the built package files. Compatibility between ES6 and TS is maintained.
yep, this method works great for libraries authored in typescript (and/or those that take the time to provide external module-style typedef files), but the @typings would be for what tsd/typings does today (mostly ambient)
If that first feature is true, it will bring me back to TypeScript. Honestly, my major deterrence of TypeScript was managing both 3rd party libraries and typing files.
I find it funny - the first language I learned was ActionScript 3 (built on ECMA 4 proposal) and I become a Flash developer aka laughing stock. I remember I had very rough time transitioning to JavaScript because it felt like a stone age compared to ActionScript 3. Then came TS and saved me and dare I say the whole web.
You should also have a look at Haxe, which comes from an ActionScript-inspired syntax, and compiles to JS (and a whole bunch of other languages, including ActionScript). It has even better language features than TypeScript (gotta love compile-time macros), although TypeScript has the benefit of adding features in an attempt to describe existing JavaScript code using types, so the interop with JS libs is easier.
I just can't justify writing my team's code base in typescript. I don't want to make that move, setting us down a very specific path. I don't want to add another layer of training to develop on our codebase, but mainly, I just don't have the confidence that it's a right or wrong choice, and its a big choice.
But I get a sense that it would be great to try for smaller, disposable projects as that's limited risk.
Does anyone else struggle with TypeScript simply because of how big a choice it can be?
You could start incrementally with Tx3's suggestion -
> One thing I would like to add regarding the .js files, TypeScript 1.8 added a flag --allowJs which makes some sanity checks to the plain old .js files.
Or burai's suggestion -
> Another good alternative is Facebook's flow. Rather than being a transpired language it works on top of JavaScript files.
It also depends on how deep do you want to go with language features.
Let's say you have existing ES2015 source code. You could start experimenting on a local machine and use a script to rename js files to ts. Run the TypeScript compiler and annotate some of the most used functions.
You can then check what is the experience of using those functions that have type annotations, what is the speed of the development, etc.
Sit down with colleagues, show what you have done and ask what do they think. You can then decide to proceed and stick with type annotations until you're sure. Use boy scouts rule: "Always leave the campground cleaner than you found it." aka add typings to the function declaration.
My impression of TypeScript, is that the .js it produces is actually extremely legible. This is especially true if you mainly use it for strict typing.
So in theory you could give it a go, and if you decide you don't like it after a while, you could continue development on your .js files and ditch the .ts files.
I wrote in a previous post that I enjoy working with Typescript and overall it's a win for our team, but these days I sometimes wonder how much of a win over es6/7.
I've found the biggest headache is getting all the *.d.ts files setup right and setting up your own types can sometimes be a pain, but as I mentioned it's pretty opt-in.
You don't use TS because of the ES6 features. If that's your goal then you can simply stick with babel. You use TS because of the type checker, and that will not be part of ES6/7.
The advantage of TypeScript is it emits idiomatic js. Decided that optional static typing is more trouble than it's worth? Fine. Bin your ts and keep the js. The code is totally fine. This is the attitude our team adopted when we first tried it out back in the 0.9 days. We didn't go back. You won't want to either
What "level of training" are you talking about? TypeScript is a JavaScript with opt-in types. Rename .js to .ts - it's a valid TypeScript file. You can gradually enrich the code base with type information, while still keeping old stuff intact.
One of the huge benefits is that you can start using "future" features (like sane variable scoping, for example) of JS now - TS will treat the code as "new", but downcompile it to what current browsers can understand.
As something of a side note on this post, suggesting there is a single 'Dart/CoffeeScript route' that languages can take seems a bit misleading.
Dart is a whole different language that happens to be transpilable to JS. CoffeeScript is just a dash of syntactic sugar that (IMHO) makes JS a whole lot less tedious to write ('The golden rule of CoffeeScript is: "It's just JavaScript"').
Notwithstanding that, I am keen to try using TypeScript on a new project one of these days.
Good point! I used similar categorising as Anders Hejlberg used in the JavaScript Jabber podcast (https://devchat.tv/js-jabber/209-jsj-typescript-with-anders-...). For those who don't like audio, he basically referred TypeScript being superset of JavaScript, unlike some other languages.
Often it feels like the CS professors I met at university, who wanted to teach you about "real programming languages and not such toys like JavaScript"
JS is very cool because of how readily available it is and the awesome ecosystem that has grown around it. That said - you don't have to be a CS professor to see that the language (especially in early incarnations) wasn't well designed, to put it nicely. A.H. knows this just like anyone with the skills to design languages and compilers.
Haha, I met him on a JS conf once. There was one of those old "JavaScript has no types and is no real language" guys and he kicked his ass.
Basically he said static typing could be good and there were languages like Scala out there with real good type systems, but most of the "pro static types"-fanboys are using crap like Java or C++.
No, I think solomatov is saying you can always use the 'any' type built-in to TypeScript. You can always do:
declare var fancyNewLib: any;
fancyNewLib('hello');
fancyNewLib.someCoolFeature = 3;
You don't get any suggestions or typechecking (obviously), but you're no worse off than just using JS in that sense. And you can easily replace your 'declare' with the actual // <reference> tag when you find it or make it, and clean up any errors.
Just changing the file extension from js to ts will often bring out bugs in your code. The type inference will often catch things. Functions that are declared multiple types or that have duplicate parameters, and a lot of other stuff will be caught.
Actually, it's better. If the core of your application has non trivial business logic, it's a good idea to write it in typed way and write ui and other support stuff with partially typed code.
Fair enough and I think with strict null type checking, where null/undefined isn't part of any anymore things would still get much better than with plain JS.
Not even Microsoft's own JavaScript libraries (e.g. Excel) have first-party type defs, at least as of a few months ago. Making matters worse, that aforementioned Excel library has polymorphic parameters everywhere. We gave it a shot with ng2 on a one-off, but honestly TypeScript just slowed us down and the juniors tried sneaking "Any spam" through code reviews.
I've often thought about a CoffeeScript variant with optional typing that transpiled to TS. The types would be ascribed via a backslash, e.g. `(hello\string) -> hello + ' world'`. But considering the huge benefit of TS is the tooling such as intellisence, I doubt it's worth the effort to start a whole separate ecosystem.
I envisioned it somewhat the opposite way. Gradual types a-la TS tacked onto CoffeeScript. No actual TS integration per-se.
Though even better would be if the TS team could figure out a way to factor out the type inference logic so that it could be applied just as simply to CoffeeScript as to Javascript, and even allow definitelytyped defs and perhaps intellisense logic to be reused everywhere.
Roslyn does this somewhat for C# and VB, so it might not be too big of a jump.
Contracts.coffee was great but sadly didn't live long. It would be worth reviving if there is an easier way to maintain it alongside mainline (iced) coffeescript.
Coffeescript was such a pain though, if something you wanted to edit was written in coffeescript, you'd have to learn coffeescript to touch it.
If something is written in typescript it doesn't matter if you don't know it. Just write what you want to change in javascript. All javascript is typescript.
"Love" is a strong word. The syntax is nicer than JavaScript, and I prefer to use it when possible. Though granted JS is making strides and the difference isn't as great as it was 5 years ago.
You're part of a MS project doing this, or you're doing it on your own?
In the blog post, I have one example of the React use case with screen capture.
I find it very helpful to have properties defined, so when I use the component, I immediately know what are the props I can provide.
There are some 3rd party React components that won't have type definitions yet, but the popular ones are well covered. I have written most of the components I have used, so I haven't had many problems.
From a React newbies's perspective, the React community is much more in the es6 camp than Typescript. Many of the starter kits are in es6. It's not a huge deal.
I actually write most of my front-end code these days in Aurelia, which is written in es6, but they actually have type annotations which Babel knows how to deal with. So basically the tooling can auto-generate the *.d.ts files for you.
That said, Typescript natively supports React syntax through TSX files, which is nice.
Shameless plug: When we've started switching to TypeScript and was looking for a good starter, we've couldn't find any that we've liked so we've created & open sourced our own. Maybe it can help people who are considering using TypeScript with React: https://github.com/barbar/vortigern
My team uses it at work and love it. Both of them were new to me when I started (was using angular in vanilla JS) and it's been a really nice experience, the type definitions definitely help with learning a new codebase. I'm thinking of introducing typescript to my personal projects at home, but so far I've been too lazy to do the initial setup work.
At the high risk of starting a huge flamewar, I think the benefits of static-typing have won in a way.
I've been using Typescript exclusively as my transpiled language for about a little over a year now. The benefits of intellisense/code-completion and refactoring because of static types can't be underestimated, especially in large apps.
That said, I actually like the opt-in nature of Typescript. There's some parts of code I don't feel like I need to define an interface for. It's just not worth it.
Typescript is turning out to be quite a powerful little language...maybe approaching the point of quite a bit of complexity, but all in-all it's pretty much been a win for my development efforts.
I found it easier to get started with Flow, but I eventually moved to TypeScript because it just has more mindshare, and isn't worse by any measure I care about.
I found it much easier to find tutorials, typings, and so on, for TypeScript. Editor support also seems to be a bit better (especially with VSCode), although the Flow-centered Nucleus plugin for Atom is pretty nice.
> I have written tests in TypeScript, compiled to JavaScript, and then used Mocha, for example, to run tests. I would like to hear your thoughts on this.
Another vote for ts-node running your tests, it makes things a lot simpler. Running tests directly instead of running compiled tests is faster and more reliable.
Mind being a bit more specific with "has some issues"? Our project isn't exactly small, and we haven't bumped into a problem yet. I'd really like to know what to expect if something is about to jump out from behind a wall.
Are there any patterns / recommended practices for unit testing Typescript. I recently joined a project which uses Angular1 + Typescript and had to introduce unit testing to the code base and it has been painful. A lot of backend API calls (wrapped in services) are being (chained and) invoked in the constructor. Any initialization is being squashed into the constructor. This makes setup of karma tests extremely painful/brittle without a complete refactor for each component.
However wrapping initialization code in public methods feel strange to me. Any suggestions?
I don't know exactly what your code looks like, but a major reason for using Dependency Injection, like AngularJS does, is to allow your tests to "mock" the injected services. Then, you verify the behaviour (but not the implementation), by checking that each mock is invoked with the expected arguments, and set it to return a particular value.
On the server side, I've used TypeMoq, which is pretty nice. I have only used it to mock imported modules; for AngularJS 1, you'd need to invoke the "inject" service, to insert your mocks into the controller.
I have found Angular 1 to be an untestable hot mess, especially on projects with a lot of technical debt. This is largely unrelated to TypeScript (and probably unrelated to Angular 1's shortcomings too).
My only suggestion is start migrating to Angular 2 immediately.
It seems like people are trying to use types as a substitute for tests and a way to protect themselves from unintended mutations.
But if you write tests already and use immutable data structures, what benefits does TS bring to people whose background isn't in strongly typed languages?
But doesn't Typescript compile to JS, thus there's actually no type-checking at runtime?
Certainly type checking can prove that it's the right type, but in my experience knowing that it's the right type but the wrong data is useless, I care if it's the exact data it's supposed to be.
How does type checking protect you (or someone else) from inadvertently mutating a value without changing it type and causing problems later? It's perfectly possible to do this without having any checks for class/instance throw a warning, unless there's something I'm missing.
> But doesn't Typescript compile to JS, thus there's actually no type-checking at runtime?
Sure, but that's what happens in every other language e.g. C's type system will enforce certain properties for you but when you compile to machine code the machine code is not checking those properties at runtime. If you know the compiler is correct you don't have to worry.
Nobody is saying you should stop writing tests but a good type system will mean you have to write less tests because the type system will do some of the checking for you in a more robust way.
You claimed that the lack of type checking at runtime is somehow detrimental to the notion. He pointed out that, fundamentally, all strongly typed languages run as weakly typed machine code at runtime, giving C as one obvious example. Thus, types in TS are at least as useful as types in C.
The concrete advantage of TS is the same as the concrete advantage of any strongly typed language - types let you do static validation, and unlike tests, they allow for that validation to be provably complete (of course, the domain of constraints that can be so validated depends on the power of the type system in question).
Yes, but what about mutating objects/primitives in a way that causes all validations to pass but still be passing the wrong data?
It seems like using type checking as a reason to write fewer tests actually just creates nastier bugs in areas where your coverage is based on type validation.
What am I missing about checking type mutation !== mutation being a problem?
What is the advantage of strong types if your data structures are immutable? Is there any? I'm genuinely trying to understand if TS is just for cases where uncontrolled mutation is the norm and test coverage is poor.
Immutability is also something you can model in the type system (not currently in Typescript, but it is possible in other languages).
That said, immutability by itself won't save you from passing the wrong shape of immutable object into a function, just as validating the shape of the argument passed and the argument expected against each other won't help if there are other assumptions being made by the code (such as that the object cannot be mutated). As one of the parent comments pointed out, types are proofs. Not everything can be proved to a compiler, but (assuming a bug-free compiler) everything which can be proved to a compiler does not need any additional tests. Things which cannot be proved to a compiler will need tests, of course.
> Yes, but what about mutating objects/primitives in a way that causes all validations to pass but still be passing the wrong data?
> What is the advantage of strong types if your data structures are immutable? Is there any? I'm genuinely trying to understand if TS is just for cases where uncontrolled mutation is the norm and test coverage is poor.
Can you give an example? I'm not following what you mean. Why would strong types be less useful if your data structures are immutable? OCaml mostly uses immutable data structures for example but its strong type system is fundamental to why it can be used to write such robust code.
Typescript has a stronger type system than JavaScript so Typescript can therefore capture more program properties statically compared to JavaScript whether you're using mutable data structures or not.
> knowing that it's the right type but the wrong data is useless, I care if it's the exact data it's supposed to be
Entirely possible people are comparing apples to oranges here. My experience is all based on structured data; e.g. User, Address, Order, Product, etc, and the relations between them. Data is generally static throughout the duration of the call; e.g. CRUD app.
It sounds like your experience is more with raw numbers and collections, and higher rates of entropy. In that case, I can see how types wouldn't add much value, and tests would be far more essential.
You are correct, there is no type-checking at runtime. Getting garbage data where you are expecting properly formed data will throw a nasty wrench in your application.
The whole point of strongly statically typed languages is that the proofs are performed at compile time. Statically typed programs shouldn't change behaviour after type erasure.
Languages with strong enough type systems can statically guarantee that data is structured correctly at runtime. Stronger structural requirements require stronger type systems. Algebraic data types are sufficient for many common static structuring guarantees.
It's not just javascript with types. The language is written in a way that makes it much easier for machines to understand your code. I'm not talking about just tests and linting.
Check out some typescript tooling videos, this one was shown at an angular 2 conference but you don't need to know any angular to watch it:
I find it funny that a lot of reasons boil down to "it's easier for the machine" -- developer happiness (which most of you mentioned, too!) through autocompletion and less failures seem to be a better selling point :)
A major benefit is the ability to quickly understand code you didn't write because you have the ability to 'go to definition' or 'find all references' which works across multiple files. On top of that once you understand the code and need to modify it you can quickly refactor across multiple files instantly and with a high degree of confidence.
Types are not a substitute for tests, but they eliminate an entire class of bugs that are screened out at compile time. That way you can focus your tests on functionality and not silly mistakes.
Which ide are you using? WebStorm, for example, gets easily confused about common JS idioms, which makes its tipped hinting useless in many situations. This makes refactoring a pain since you can't perfectly trust its warnings.
- Documentation. You don't need to have free-text comments saying "@param myArg must be a string", you can just look at the type signature, and immediately know how to use a function. Attempting to pass in a number will fail at the time of writing, not at runtime (of your test). This is a huge benefit when using third party libraries. For a JS lib, you need to read the documentation, and if something is undocumented you need access to the source code and the time to analyse it, to understand exactly how to use the lib. Compare this to making the type interface available.
- It's easier for a machine to read, which means the possibility for better tooling. e.g. linters and checkers, automatic refactoring.
- It's actually faster to write, due to the ease of making changes. If you move a function out to a new module, the compiler will immediately tell you all of the invocation sites that are now broken. (If you rely on tests, you have to make sure you have tests in every invoking module, rather than just the module you are actually changing. [Ignoring mocking issues...])
I'm not so sure about the "faster to write" since my IDE and linting will already take care of that. The java-style boilerplate is a huge turnoff for me, and the validation seems to produce false negatives so often as to actually introduce more bugs. Ie, If a variable gets mutated without changing type, TS is now worse than useless for validation.
The self-documenting part is the strongest argument I've heard, but writing real documentation and real tests still seems better.
An IDE can assist you even better when it can understand the code you're writing. For example, autocomplete "myString.re" without type hints, and an IDE like IntelliJ might suggest the method "reject()" (which is found on a Promise interface), or "remove()" (which is found on a DB entity interface), as well as "replace()" (which is found on a String interface). Wouldn't it be nice for it to always pick the right option?
TypeScript doesn't help with immutability at all. For that, you should look at something like PureScript. (Or GHCJS, or any other compile-to-JS languages that support immutability.) TypeScript assumes you are sticking with JavaScript's semantics, mutability and all. If you want immutability, you still need to use "Object.freeze()". Variable mutation is not something picked up by types alone.
There are other languages that introduce "dependent types", which can actually include values in their type signatures, which might be able to specify something like you want; i.e. "a function that accepts an argument of type 'a' which derives a number, and returns the same type 'a', but the returned value must be equal to the input value plus 10".
Documentation is nice... or so I've heard! Speaking for myself, developers are lazy and just want to write code. Why not make them document the code as they write it? Plus, external documentation (even as JavaDoc-style comments) always falls out of date with the code.
I think better than tests, is to enforce "design by contract" - assert your preconditions and postconditions on every function. This inlines your assumptions and behaviour verification with the actual code to be run. (If performance is a concern, you can implement this in a way that allows you to disable assertions.)
In general, the more guarantees you can enforce at compile time (such as only accepting certain types of values, or that input values are never mutated in place), the less need there is for having twice as much testing code as application code.
Refactoring. Say you want to make a 1:1 relation into a 1:* relation. You change your core data type from e.g. `type person{address:string}` to `type person{addresses:string[]}`, follow/fix all the red squiggly lines until it compiles, and generally you're done.
OTOH with unit tests, you've got to find/fix not only your app code but the test code as well. You may forget some things because you overlook a function and its tests. And of course your test coverage is likely not 100% so things may slip through.
So in addition to "refactoring", I'd add "guarantees": literally one line of code gives you 100% coverage.
PLUS autocomplete. JS editors have gotten better, but TS blows them out of the water. Importa new library, and stuff almost codes itself.
Mutability is somewhat orthogonal to the discussion. You can write things immutably or immutably in typed or untyped languages. I prefer mostly immutable too. But, going with it, I'd been a Clojure fan for a couple years, where immutability and dynamic types are both forefront. After rewriting an app in F#, and then performing a major refactor, this is what I had to say about it. https://disqus.com/home/discussion/owenrh/the_beauty_of_cloj... tldr: refactoring is much easier with types backing you up.
n.b. There's no reason you have to choose. You can have all three. Immutability, tests, and types. I do. Though if I had to choose, I'd pick types first. I'm a disciplined enough coder that immutability guarantees aren't that necessary, and if I've got types and discipline, then tests usually are unnecessary.
- Replace all "var x = require()" calls to "import x = require()"
- Gradually refactor components (controllers, directives, services) to classes, or at least add type annotations to everything.
Because TypeScript is a superset of JavaScript, you can work on an untyped system and add gradual typing until the codebase starts looking nicer. Any new functionality can be implemented in TypeScript; any existing functionality can be slowly refactored when it is touched by changes.
I'd add tslint to that once the basic migration is done. It can get a lot of issues in the code that are either bad stylistic options, or things that can produce errors in the future.
193 comments
[ 3.1 ms ] story [ 257 ms ] threadThis two tools are amazing and it improves working with JavaScript to the point of never wanting to go back to not typed JavaScript
One thing I would like to add regarding the .js files, TypeScript 1.8 added a flag --allowJs which makes some sanity checks to the plain old .js files.
Your existing minification step will remove the comments. But even if you don't remove the comments the code will still run.
JS Jabber episode was great! Full of great insights.
Emacs is capable of all of these things (because Emacs is capable of anything) but it doesn't work that way out of the box nor is there an already-written package around that makes that easy. To rename you type M-x tide-rename-symbol and it's inconvenient enough that I forget to use it.
And throw the bind into your .emacs
At the same time, you're gonna need to use some preexisting libraries, and run into the issue of not having types available. (though they do exist for most mainstream libs) However, I found creating types for any given lib pretty flexible, and that you can usually get away with just enough types.
The way I dealt (until proper type definitions are written) was:
declare module "react-loader" { var noTypeInfoYet: any; export = noTypeInfoYet; }
(TBH Angular1 was pretty great at the time, but after React I look back at those monstrous template files, ng-everything, plugins and workarounds, and only the bad memories surface).
I tried React but I hit a wall of needing synchronisation, routing, notifications, can you do that now?
Sure I should have checked it out but the documentation didn't exactly highlight it (at the time anyway), and with the services, controllers, testability, etc it just looked like they'd thought it all through.
Frankly after finding React there's no way I'm giving NG2 even a look in.
In the last 4 months I've built a large angular1.5 app in ES5 using the new component facade and d3.js, and it's been pretty smooth.
Two of the main problems I had with angular - the template syntax and the way the digest cycle worked, both contributed to its scaling factor.
The template syntax meant you would always wonder what a parameter meant when passed to a directive. Essentially, all parameters passed to a directive were strings, but angular would parse some strings to mean "the property of a parent scope", and some more convoluted variations of that(check out https://docs.angularjs.org/api/ng/service/$compile#-scope-). Furthermore, using the recommended ng-controller directive created great spaghetti code, with template blobs where the current scope is almost impossible to track. Applying multiple directives to the same element sometimes required special parameter parsing, which further made the situation confusing. This confusion meant the more stuff you had, the more time you had to spend carefully memorizing it all so you knew what was going on.
The digest cycle fundamentally limits angular's scale. It was designed with some scale in mind at first - a digest cycle triggered at a specific scope would only run watchers under that scope, thus limiting the effect. I assume they hit the problem that there was no way to define explicitly "global" watchers, or to tell another scope to update itself. This issue apparently crept into the development of angular and local digests were pretty much abandoned in favor of using scope.$apply(), which triggers a digest cycle at the topmost scope. Local digests were not really talked about in the documentation, which was littered with examples of scope.$apply(), all of angular's provided directives used scope.$apply() and pretty much no one wrote any code limiting the amount of watchers triggered in an update. React specifically addressed this point with shouldComponentUpdate and the ecosystem built around it, which is one of its biggest performance benefits.
Further criticism would be the terminology confusion. Angular talked about directives, modules, controllers, filters, services, factories, providers, etc.
In essence, providers/factories/services were all just singletons instantiated in different ways(providers being the progenitor and factories/services being variations).
Filters were poorly named, as they were actually mapping functions or value formatters. Thus came the infamous "filter filter", which filtered elements in a collection, as opposed to say the "currency filter", which added currency markers to numbers. They were also incredibly inefficient, running on every digest cycle, even after the attempted fixes with "stateless filters"(http://www.bennadel.com/blog/2766-stateless-filters-don-t-ap...)
Controllers were essentially stunted directives, provided via the ng-controller directive. They were paraded as the way to start "simple" development, but led to a lot of bikeshedding about where to use controllers and where directives, the confusion around directives requiring controllers and the limitations around that, how to appropriately bind directive parameters to a controller's "scope"(not an actual scope but the controller function object itself), the bindToController function resolving that problem(introduced in 1.4?), etc, etc.
Modules were also a weird concept without much practical use. They do not provide a separate namespace(all your angular elements go into the same one), they don't leverage angular tooling to import your code(you have to import your code manually e.g. add script tags). Mostly they provided the config() and run() functions which would let you sched...
Sadly I'm stuck having to deal with it, and I can honestly say I hate it and it makes me want to cry. Might have to do with the way it was used, though. I find myself having to write a directive each time I have to populate a fucking select2.
At the time it was compared to jQuery spaghetti code and Backbone with a little less jQuery spaghetti code.
It brought front-end web frameworks to the masses.
Granted I think they should've started Angular 2.0 a year earlier, but it was great at the time. Sadly is has aged very poorly.
You should aim for deployment process where outDir would be in .gitignore and building of the JS files would happen on a build server.
This is the kind of stuff that makes the "incremental" adoption argument hard for me to swallow. If I can't rewrite a .js file to .ts without any further ripple effects within the project, then it's not truly incremental.
I wrestled with this stuff last week and ultimately ended up going with flow since it gave me type checking _and_ incremental adoption, without complicating my existing babel/webpack stack.
Your .js files should typically be relative to each other, so unless you're using absolute paths (which you usually shouldn't!), this hasn't been a problem for other users. Is there something that I'm missing?
I want the ability to convert any existing js file within the project to ts without having to touch a single other source file. I also want the type checker to assume that if I don't have a type definition for a package, that I don't want the use of that package to be type checked (without being forced to rewrite my ES6 imports to commonjs).
I was able to do this with flow but not typescript.
Is there something I'm missing?
Let me know how that ends up working out!
[1]: https://github.com/TypeStrong/ts-loader [2]: https://github.com/s-panferov/awesome-typescript-loader
A.ts importing B.ts would be `import * as A from "./B"`, where the transpiled A.js file would import from `./B.js` rather than the original A.ts file.
Furthermore you can mix JS into your compilation process and the TS compiler will* accommodate you by compiling your JS alongside your TS code.[1]
* TypeScript 1.8
[1] https://github.com/Microsoft/TypeScript/issues/4792
>[ts] Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option to remove this warning.
When using the angular-cli,
What tools are you talking about?
A good example of this is modules. Angular 1 had its own module syntax (partly because modules weren't really a thing when it started). You could write scripts, concat them together and go.
In angular2 we embraced ES6 modules, which means a developer has to deal with loading and bundling etc.
One nice side effect here though is we can leverage more tooling from the rest of the JS community (eg, the great work of webpack) rather than having angular-specific solutions.
The reason is simple, contributing and reading the project source code should not start by learning a programming language. It is a different scenario when a team in a company has decided to learn and write their project with TypeScript/CoffeeScript/etc. language.
About tooling, I think writing modern JavaScript (ES2015, ES2016) is beneficial and therefore, you need a tool like Babel. When you go to that path, why not use TypeScript instead and get ES2015/ES2016 + type system + great compiler?
It may still be a slam dunk to you, but it's not as "obvious" an answer as you make it sound.
[1] https://www.typescriptlang.org/docs/handbook/advanced-types.... [2] https://github.com/Microsoft/TypeScript/pull/7140
As a Scala developer I like Typescript a lot. Which is a first for me in browser development.
But I'm not so fond of webpack et al. So I created a sbt-typescript plugin. And now I have incremental compilation of NG2 - Play apps for both the browser side and the server side. In a single build definition. It's a nice experience.
Thanks for the links, future looks promising indeed.
Interesting, choosing TypeScript over Scala.js; what does TS offer vs. working directly in Scala across the board?
The only downside is lack of "isomorphic" code reuse which I gladly accept anyways because I rarely see server and client side code reuse as beneficial beyond simple models and validation.
I could imagine, that most JavaScript developers prefer this over nominative systems.
Even "6to5" (previous name for Babel), which originally had as a design goal that it would emit good JS (and back then, it did!) quickly lost that when they tried to aim for spec correctness.
TypeScript actually punts on the standard in some cases (eg: TypeScript classes are not ES6 compliant) to generate more readable code, but it's still not "good" code and I certainly would never consider working with that.
- moving types to npm (despite the great work of Blake on typings, managing type definitions is still out of band) - npm install @types/somelib will be great.
- AST transforms, which will open up some interesting options for bundling and optimizations, sort of like babel's plugin ecosystem.
https://github.com/ihsw/toxiproxy-node-client/blob/master/pa...
An NPM package will be typed and you won't have to download typings separately. It may be improper to publish typings with your code instead of letting the user install them separately, but at that point it ceases being clean.
In this case, my `toxiproxy-node-client` package is written in TypeScript and the typings are published alongside the built package files. Compatibility between ES6 and TS is maintained.
Still, the hostility from the "against" side (which also included Yahoo and a pre-Chrome Google) was enough to derail the effort.
But I get a sense that it would be great to try for smaller, disposable projects as that's limited risk.
Does anyone else struggle with TypeScript simply because of how big a choice it can be?
> One thing I would like to add regarding the .js files, TypeScript 1.8 added a flag --allowJs which makes some sanity checks to the plain old .js files.
Or burai's suggestion -
> Another good alternative is Facebook's flow. Rather than being a transpired language it works on top of JavaScript files.
Let's say you have existing ES2015 source code. You could start experimenting on a local machine and use a script to rename js files to ts. Run the TypeScript compiler and annotate some of the most used functions.
You can then check what is the experience of using those functions that have type annotations, what is the speed of the development, etc.
Sit down with colleagues, show what you have done and ask what do they think. You can then decide to proceed and stick with type annotations until you're sure. Use boy scouts rule: "Always leave the campground cleaner than you found it." aka add typings to the function declaration.
So in theory you could give it a go, and if you decide you don't like it after a while, you could continue development on your .js files and ditch the .ts files.
Chances are that you'll love TypeScript though.
I've found the biggest headache is getting all the *.d.ts files setup right and setting up your own types can sometimes be a pain, but as I mentioned it's pretty opt-in.
One of the huge benefits is that you can start using "future" features (like sane variable scoping, for example) of JS now - TS will treat the code as "new", but downcompile it to what current browsers can understand.
Dart is a whole different language that happens to be transpilable to JS. CoffeeScript is just a dash of syntactic sugar that (IMHO) makes JS a whole lot less tedious to write ('The golden rule of CoffeeScript is: "It's just JavaScript"').
Notwithstanding that, I am keen to try using TypeScript on a new project one of these days.
Often it feels like the CS professors I met at university, who wanted to teach you about "real programming languages and not such toys like JavaScript"
Basically he said static typing could be good and there were languages like Scala out there with real good type systems, but most of the "pro static types"-fanboys are using crap like Java or C++.
Also VSC often is flunky with picking up defs... sometimes you open a file and some are missing.
Or imports with numbers don't work.
On the other hand, using big libs like babylonjs is charming with all the auto complete :)
https://www.edx.org/course/introduction-typescript-microsoft...
Though even better would be if the TS team could figure out a way to factor out the type inference logic so that it could be applied just as simply to CoffeeScript as to Javascript, and even allow definitelytyped defs and perhaps intellisense logic to be reused everywhere.
Roslyn does this somewhat for C# and VB, so it might not be too big of a jump.
http://disnetdev.com/contracts.coffee/
If something is written in typescript it doesn't matter if you don't know it. Just write what you want to change in javascript. All javascript is typescript.
You're part of a MS project doing this, or you're doing it on your own?
I find it very helpful to have properties defined, so when I use the component, I immediately know what are the props I can provide.
There are some 3rd party React components that won't have type definitions yet, but the popular ones are well covered. I have written most of the components I have used, so I haven't had many problems.
I actually write most of my front-end code these days in Aurelia, which is written in es6, but they actually have type annotations which Babel knows how to deal with. So basically the tooling can auto-generate the *.d.ts files for you.
That said, Typescript natively supports React syntax through TSX files, which is nice.
I've been using Typescript exclusively as my transpiled language for about a little over a year now. The benefits of intellisense/code-completion and refactoring because of static types can't be underestimated, especially in large apps.
That said, I actually like the opt-in nature of Typescript. There's some parts of code I don't feel like I need to define an interface for. It's just not worth it.
Typescript is turning out to be quite a powerful little language...maybe approaching the point of quite a bit of complexity, but all in-all it's pretty much been a win for my development efforts.
Here's two things I saw on Jeff Morrison's (Flow author) Twitter:
* http://djcordhose.github.io/flow-vs-typescript/2016_hhjs.htm...
* https://gist.github.com/jeffmo/ef9214ca3acfe76c54a237b5710d3...
"Flow written in OCaml, Typescript in Typescript"
I found it much easier to find tutorials, typings, and so on, for TypeScript. Editor support also seems to be a bit better (especially with VSCode), although the Flow-centered Nucleus plugin for Atom is pretty nice.
We use ts-node to run our mocha tests without ever compiling: https://github.com/TypeStrong/ts-node
On the server side, I've used TypeMoq, which is pretty nice. I have only used it to mock imported modules; for AngularJS 1, you'd need to invoke the "inject" service, to insert your mocks into the controller.
Further reading:
https://en.wikipedia.org/wiki/Mock_object
My only suggestion is start migrating to Angular 2 immediately.
But if you write tests already and use immutable data structures, what benefits does TS bring to people whose background isn't in strongly typed languages?
Certainly type checking can prove that it's the right type, but in my experience knowing that it's the right type but the wrong data is useless, I care if it's the exact data it's supposed to be.
How does type checking protect you (or someone else) from inadvertently mutating a value without changing it type and causing problems later? It's perfectly possible to do this without having any checks for class/instance throw a warning, unless there's something I'm missing.
Sure, but that's what happens in every other language e.g. C's type system will enforce certain properties for you but when you compile to machine code the machine code is not checking those properties at runtime. If you know the compiler is correct you don't have to worry.
Nobody is saying you should stop writing tests but a good type system will mean you have to write less tests because the type system will do some of the checking for you in a more robust way.
A: C's type system is very useful for data validation.
Please read at least one sentence before writing a response next time.
The concrete advantage of TS is the same as the concrete advantage of any strongly typed language - types let you do static validation, and unlike tests, they allow for that validation to be provably complete (of course, the domain of constraints that can be so validated depends on the power of the type system in question).
It seems like using type checking as a reason to write fewer tests actually just creates nastier bugs in areas where your coverage is based on type validation.
What am I missing about checking type mutation !== mutation being a problem?
What is the advantage of strong types if your data structures are immutable? Is there any? I'm genuinely trying to understand if TS is just for cases where uncontrolled mutation is the norm and test coverage is poor.
That said, immutability by itself won't save you from passing the wrong shape of immutable object into a function, just as validating the shape of the argument passed and the argument expected against each other won't help if there are other assumptions being made by the code (such as that the object cannot be mutated). As one of the parent comments pointed out, types are proofs. Not everything can be proved to a compiler, but (assuming a bug-free compiler) everything which can be proved to a compiler does not need any additional tests. Things which cannot be proved to a compiler will need tests, of course.
Can you give an example? I'm not following what you mean. Why would strong types be less useful if your data structures are immutable? OCaml mostly uses immutable data structures for example but its strong type system is fundamental to why it can be used to write such robust code.
Typescript has a stronger type system than JavaScript so Typescript can therefore capture more program properties statically compared to JavaScript whether you're using mutable data structures or not.
Entirely possible people are comparing apples to oranges here. My experience is all based on structured data; e.g. User, Address, Order, Product, etc, and the relations between them. Data is generally static throughout the duration of the call; e.g. CRUD app.
It sounds like your experience is more with raw numbers and collections, and higher rates of entropy. In that case, I can see how types wouldn't add much value, and tests would be far more essential.
Thankfully this rarely happens.
Languages with strong enough type systems can statically guarantee that data is structured correctly at runtime. Stronger structural requirements require stronger type systems. Algebraic data types are sufficient for many common static structuring guarantees.
Check out some typescript tooling videos, this one was shown at an angular 2 conference but you don't need to know any angular to watch it:
https://www.youtube.com/watch?v=e3djIqAGqZo
Types are not a substitute for tests, but they eliminate an entire class of bugs that are screened out at compile time. That way you can focus your tests on functionality and not silly mistakes.
- It's easier for a machine to read, which means the possibility for better tooling. e.g. linters and checkers, automatic refactoring.
- It's actually faster to write, due to the ease of making changes. If you move a function out to a new module, the compiler will immediately tell you all of the invocation sites that are now broken. (If you rely on tests, you have to make sure you have tests in every invoking module, rather than just the module you are actually changing. [Ignoring mocking issues...])
The self-documenting part is the strongest argument I've heard, but writing real documentation and real tests still seems better.
TypeScript doesn't help with immutability at all. For that, you should look at something like PureScript. (Or GHCJS, or any other compile-to-JS languages that support immutability.) TypeScript assumes you are sticking with JavaScript's semantics, mutability and all. If you want immutability, you still need to use "Object.freeze()". Variable mutation is not something picked up by types alone.
There are other languages that introduce "dependent types", which can actually include values in their type signatures, which might be able to specify something like you want; i.e. "a function that accepts an argument of type 'a' which derives a number, and returns the same type 'a', but the returned value must be equal to the input value plus 10".
Documentation is nice... or so I've heard! Speaking for myself, developers are lazy and just want to write code. Why not make them document the code as they write it? Plus, external documentation (even as JavaDoc-style comments) always falls out of date with the code.
I think better than tests, is to enforce "design by contract" - assert your preconditions and postconditions on every function. This inlines your assumptions and behaviour verification with the actual code to be run. (If performance is a concern, you can implement this in a way that allows you to disable assertions.)
In general, the more guarantees you can enforce at compile time (such as only accepting certain types of values, or that input values are never mutated in place), the less need there is for having twice as much testing code as application code.
OTOH with unit tests, you've got to find/fix not only your app code but the test code as well. You may forget some things because you overlook a function and its tests. And of course your test coverage is likely not 100% so things may slip through.
So in addition to "refactoring", I'd add "guarantees": literally one line of code gives you 100% coverage.
PLUS autocomplete. JS editors have gotten better, but TS blows them out of the water. Importa new library, and stuff almost codes itself.
Mutability is somewhat orthogonal to the discussion. You can write things immutably or immutably in typed or untyped languages. I prefer mostly immutable too. But, going with it, I'd been a Clojure fan for a couple years, where immutability and dynamic types are both forefront. After rewriting an app in F#, and then performing a major refactor, this is what I had to say about it. https://disqus.com/home/discussion/owenrh/the_beauty_of_cloj... tldr: refactoring is much easier with types backing you up.
n.b. There's no reason you have to choose. You can have all three. Immutability, tests, and types. I do. Though if I had to choose, I'd pick types first. I'm a disciplined enough coder that immutability guarantees aren't that necessary, and if I've got types and discipline, then tests usually are unnecessary.
Talking specifically Angular 1 if you have one, otherwise a general tutorial is fine.
I'm interested in whether it is worth the effort migrating a pretty large frontend codebase.
- Set up your TypeScript config
- Set up Typings (or some other type def manager)
- Rename all ".js" files to ".ts"
- Replace all "var x = require()" calls to "import x = require()"
- Gradually refactor components (controllers, directives, services) to classes, or at least add type annotations to everything.
Because TypeScript is a superset of JavaScript, you can work on an untyped system and add gradual typing until the codebase starts looking nicer. Any new functionality can be implemented in TypeScript; any existing functionality can be slowly refactored when it is touched by changes.
Understatement of the century.