Hacker News new | past | comments | ask | show | jobs | submit login
Porting 30K lines of code from Flow to TypeScript (davidgom.es)
434 points by munchor on Jan 14, 2019 | hide | past | favorite | 227 comments

I recently did the exact same job as the author of this post.

Migrating 15K LoC from JS to TS.

The author of Vue.JS also migrated Vue completely to Typescript.

At first I had major apprehension because of how much Microsoft generally enforces things on developers.

It's well know that if you start using C# , your entire stack will generally be MS based...(SQL Server, Azure etc... )

But after I did the migration , I was blown away by how confident and how much flexibility I had when i was writing my code.

Even if I have been writing code with Java / C# for nearly a decade , nothing has come close to Typescript in terms of productivity,flexibility and confidence.

Having used Javascript since before Node.JS , I think the whole idea of having to "transpile" my code to something or to respect some "rules" define by a company with a reputation that wasn't really "all in open source" .

But after using Typescript on multiples projects , you just can't go back , it's incredible how well it's scale without enforcing anything on the developers.

Hopefully , one day bootcamps will include Typescript in their trainings to demonstrate how typings can solve maintainability issues...

"Even if I have been writing code with Java / C# for nearly a decade , nothing has come close to Typescript in terms of productivity,flexibility and confidence."

Same here.

Isn't this truly amazing?

Take a jangly language like JS, and add some typing for the compiler, which forces you to write cleaner code in addition to all the compiler advantages ... combined with some really cool features and bang a magical, pragmatic language.

Aside for some script things for which Python is still a blessing, I'd chose TS for everything else, at least to start.

It just has the right mix of flexibility and expressivity etc..

Though TS is an MS project, I suggest it really is quite different, it's 'open from the start' kind of thing, you can have a loot at TSC internals. The team seems to be fairly dynamic and responsive.

Typscript is my #1 favorite 'invention' of the last few years, I think it will be around for a while, and I hope to see many more JS API's 'properly documented' with the help of TS.

> Take a jangly language like JS, and add some typing for the compiler, which forces you to write cleaner code in addition to all the compiler advantages ... combined with some really cool features and bang a magical, pragmatic language.

Agreed. And when you compare how TS was designed to how other languages were designed, it makes a lot of sense why TS ended up being such a great language.

Unlike virtually every other language in existence, TS was not designed from the ground up. It was designed with a very specific goal in mind: to adhere to the standards that JS developers had already converged on. So when a JS developer said if (foo), TS was built to realize that was an implicit type check against null. When a JS developer said if (obj.type === “bar”), TS read that as a way to ensure obj was in fact a bar.

It’s unlikely that a language developer working from scratch would have come up with these odd idioms, let alone prioritized their inclusion into a new language. But in the case of TS, all these idioms came from millions of lines of working code.

I think that’s why TS feels so enjoyable and easy to work with nowadays, whereas even the best of other languages feel a little clunky at times. It was developed prioritizing making real actual code idioms people wanted to do work.

> when you compare how TS was designed to how other languages were designed

Is it safe to say that Microsoft has probably created/designed more programming languages than any other company? I can think of a dozen off the top of my head...

Anders Hejlsberg, the lead architect behind TypeScript has created C# and J++ at Microsoft, and Delphi and Turbo Pascal before. It's safe to say he has some experience in designing languages.

J++ is one of the primary reasons I am so wary about using a Microsoft-invented language. "Embrace and extend" a well-used language with extra features (then make them only work well on Windows later) has always been a classic Microsoft strategy. If they could get away with it with Typescript, I'm sure they would try again.

Thank you! This is the argument that made me give TypeScript a try. I work in Kotlin day to day which tries to fix its base language (and batteries included with it) while not fixing it too much. TS sounded like a language in that vein.

The exception I'd add is that JavaScript's standard library is still very weak. I find myself leaning on Lodash for functionality that would be built into any other language.

Part of me would love for TS to build one out and add shims as part of the transpilation process, but it would go miles out of the scope of what TS is trying to do. And I love that (compared to Babel) TS has no plugins and relatively few options.

To add onto this, using lodash + TS is not the most pleasant experience. A lot of that has to due with current limitations of the type system (mostly variadic types [0]), but I find myself having to provide lots of generics rather than relying on inference. The overloads are not great.

I say this all with recognition that lodash greatly predates TS, and the maintainers have done an absolutely wonderful job in keeping up with the overall ecosystem (ESM, typings files, etc). I can't even begin to comprehend the amount of work that has gone in to keeping lodash so modern.

That being said, I wish the interaction was slightly better, since a lot of people just immediately bring in Lodash to any JS project.

[0] https://github.com/Microsoft/TypeScript/issues/5453

I have the same experience; I wonder if anyone is working on a TS-friendly utility library, or if our best hope is the work on TS itself on [0]?

I wonder if ramda is any better in that regard?

I love Ramda and use it everywhere but sadly, it's somewhat lacking when it comes to type definitions. Everything in Ramda is curried and the types just don't reflect that very well so you get quite a few incorrect compiler errors.

Yeah, I remember liking the auto-currying, that makes sense it has a downside here.

Shouldn't tuples in rest parameters from TypeScript 3.0 [0] fix that?

[0] https://www.typescriptlang.org/docs/handbook/release-notes/t...

Not entirely.

For example, a function which takes a property path (in the form of an array of strings) and an object as parameters and returns the value at that path inside the object still needs overloads for every single tuple length.

  type Key = string | number | symbol;
  function get<T, K extends keyof T>(path: [K], obj: T): T[K];
  function get<T, K1 extends keyof T, K2 extends keyof T[K1]>(path: [K1, K2], obj: T): T[K1][K2];
  function get<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(path: [K1, K2, K3], obj: T): T[K1][K2][K3];
  function get<T, K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]>(path: [K1, K2, K3, K4], obj: T): T[K1][K2][K3][K4];
  function get(path: Key[], obj: any) {
      if (path.length === 0) {
          return obj;
      const key = path[0] as Key;
      const rest = path.slice(1) as [Key];
      return get(rest, obj[key]);
  interface Foo {
      foo: {
          bar: {
              baz: {
                  val: number;
  declare const foo: Foo;
  const num = get(["foo", "bar", "baz", "val"], foo);
In this case, num is implicitly typed as number, but if the path would be longer, you'd need to add more overloads.

I keep hearing that but I dunno. I’m building a fairly large side project in Express + React and I keep finding ways of doing things in ES6 - lodash is still not to be found in package.json. I think I might cave in eventually but as it stands my code feels clean and succinct and it’s all done in modern functional style.

One example is doing deep comparison on objects. ES6 still has no equivalent for that, and it's something you can do in one line in other languages.

Which languages? I'd think it's rare because I know no such standard function from the top of my head.

Swift immediately comes to mind - structs are value types. Also, Kotlin has data classes.

`util.deepStrictEqual` in Node at least

This is a very good point.

I wish lodashy functions, and a few other things (Time?), were just integrated into JS, in a thoughtful way ... as part of the standard lib. So they could all be done in native code in the engine.

I’m sympathetic to this point of view, but I find lodash to be too huge of an API. Most of the collection methods can be done as a small _.reduce, and I find the many method names and subtle distinctions to add more trivia than they remove.

The good news is that you can use lodash as an ES6 module, with any tree-shake capable bundler stripping out all the crap. Absolutely agree it's not optimal though.

This is a pain point that the community wishes to address: https://github.com/tc39/proposal-javascript-standard-library

I don’t see this going very far for historical reasons. This idea has been suggested for over a decade, but back then the focus of inspiration was jQuery. The false argument was that everybody was requesting it into pages anyways so it should be part of the language. Not only was this idea proposed on top of a faulty assumption but there were also performance penalties and hidden conformance defects.

These suggestions fall apart over time because they aren’t environment agnostic and lack objectivity. The subjectivity in question, “I want feature X”, usually doesn’t take alternate positions into consideration.

The proposal explicitly states that everything in the standard library would not be tied to specific target environments:

Such a library would only cover features which would be useful in JavaScript in general, not things which are tied to the web platform. (A good heuristic: if something would make sense on a web browser but not in node or on embedded devices or robots, it probably isn't in scope.)

Truth be said, jQuery can be seen obsolete because most of the functionality has been moved inside the DOM or the ajax "standard library" ...

This certainly wasn't a popular opinion 10 years ago. The history lesson here is that popular is a subjective quality that does not provide objective benefits, which is clear in a future time.

Well, as I said you could think that is jQuery "has been added" to the javascript expected api. Some functions calls has been renamed, others have been superseded, but the "vanillajs" of 201x is pretty different from the 199x - and more similar to the jQuery usage.

jQuery rise (but also underscore and others "low level libs) was interwined with the need of common functionalities, such as the DOM manipolation and the ajax calls. I still remember when all you could use was the .innerHTML property and the "new" DOM level 1 api [ https://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ ]. jQuery selectors were so game-changing that the "new" DOM api were basically copied.

I understand how incredibly difficult is to find a good balance between an "anemic" stdlib (like the barely sufficient c stdlib) to the giant ones (like java has had), moreso that a bad/poorly secured function should still be mantained "forever" for backward compatibility.

Still I think that this movement should go forward to finally have some form of "batteries included"

I guess I disagree as I much prefer Ramdas style over Lodash.. Is your concern about performance? Because I don't think it would be a massive improvement over JS code.

Curious what you're still often using lodash for?

One thing that comes to mind is comparison - array equality, deep object comparison, that sort of thing.

> Aside for some script things for which Python is still a blessing, I'd chose TS for everything else, at least to start.

I really wish Python had a decent type system; hopefully the Mypy project takes a leaf from TypeScript's book.

Also, I've found that Go does for Python much of what TypeScript does for JavaScript. Of course, Go's APIs are different than Python's and its type system is less expressive than TypeScript so it's not a perfect analog, but Go also brings speed, parallelism, sane concurrency (Python's async is a hot mess by comparison), sane deployment, and great editor integrations, which are things I sorely miss when writing Python.

Mypy is already quite powerful and understands a lot of the language. With latest versions of the language, what is handled by "transpiling" to JS, can be done "natively", ie. there is no need for an intermediate step to strip out the additional syntax for declaring types as it's now built in.

Mypy and TC and Flow and many other similar projects seem to stem from the academic work on gradual typing from nearly 20 years ago. What we're seeing now is "practical application" of that research, and while different projects have different budgets, it won't be surprising if they all converge in terms of features at some point. I'm not an expert, by any means, but we've already seen this happening many times with languages and software platforms. At least this time the point of convergence is based on comp-sci research and not on the marketing one.

My grievance is mostly that Mypy is much less polished than TS and Flow (you can argue that it's a lack of investment and that's probably true, but it doesn't do me any good as a Python developer). Notably, Mypy's syntax is very much shoehorned in. How do I define a TypeVar for a particular method in a class? If I define a TypeVar (<T>) in the class scope, does every reference to T take on the same type? I can't honestly tell by looking if it's safe to use <T> for each generic method in a class or if the type system will try to unify them to the same type. Also, does Mypy support recursive types yet? Can I meaningfully define a JSON type without hacks (e.g., `Dict[str, Any]`)? Also, can I stub out generated types yet? IIRC, the official position for SQLAlchemy was something like "eh, it's out of scope for the project". Pretty sure there was a lot of problems with defining different kinds of callables (maybe those with args and kwargs?) not to mention a bunch of ergonomic problems because they don't want to extend the parser much (it appears they take the view that it's better to have terrible syntax that is "valid Python" without much modification to the Python parser--to be clear, this isn't referring to angle brackets vs square brackets, but things like using variable declaration syntax in an outer scope to declare a type variable for a generic context).

Python's type annotation syntax is mostly similar to TypeScript.

Each method is independent unless the class is a Generic[T].

You can define a recursive type by quoting the nested reference.

You can define a JSON type with recursive types.

You can create stubs. Some are in typeshed.[1]

Mypy recently added protocols to simplify defining callable types.

Python has always been slow to add new syntax. I think that's a good thing overall.

[1] https://github.com/python/typeshed/

> You can define a recursive type by quoting the nested reference.

No, that’s not sufficient, or at least it wasn’t. The problem wasn’t symbol resolution, but Mypy actually gave you a recursion error. At one time they were intending to fix it by implementing protocols which hadn’t landed last I checked.

> You can create stubs. Some are in typeshed.

You can, but not for magical libraries like SQLAlchemy.

> Python has always been slow to add new syntax. I think that's a good thing overall.

Probably, but it’s hindering their typing story. Typescript solves the problem by building a syntax that compiled to JS, but Python has some syntax support but lots of things are shoehorned in.

I think the biggest win of Go over Python is around the single binary distribution, which I guess you included with deployments. But it truly deserves a mention on its own and it’s a major point over other (most?) languages. It really makes up for a lot of the shortcomings of Go as a language IMO. But I enjoy Go best for building system tools, the single binary doesn’t shine as much once you involve containers at which point the friction between say a Python/Node container and a Go container is pretty much the same

That’s definitely up there. As for containers, I think Go’s static compilation story actually has some important ramifications for containers, namely I don’t need a set of base images for doing development since I can just toss a binary into the container and run it there (running in the container is useful because our local dev setup uses Docker Compose to orchestrate the microservices). If we could do this with Python it would simplify our CI and our development process by a good bit. And of course Go images are about a factor of magnitude smaller than Python images to boot!

Turns out creating a static binary distribution can be done in Python without containers [0].

[0] http://www.pyinstaller.org/

The tiny size of an Alpine + Go container is still nice compared to a typical Node or Python container, though.

> Take a jangly language like JS, and add some typing for the compiler

It so turns out that there is a great sweet spot in between the straight jacket typing of classic Java/C# and the loose "do way ever you like" approach in Python/JS.

I don't want use another language that doesn't have optional typing and structural typing. It combines the best of both ends of the spectrum.

My thinking exactly.

Wow, almost...almost reminds me of a little known language called ActionScript 3. ;)

As someone who hated ActionScript 3, I vehemently disagree. AS3 felt like some Java guys trying to force a Java mindset on a language that didn't really suit it.

And I say that as a Java guy myself. TS is much more dynamic and less abrasive, IMO.

Funny, I feel the same way about TS :) To be fair tho, you’re right. Flex was a very Java centric endeavor, from its compiler to the first big frameworks (Caignorm?) all the way to its target audience, which were enterprise companies that wanted to do their “thing” on the web. There was a massive developer migration from the Java/enterprise world to the AS3 ecosystem that had a visible impact. Also to be fair, if you look at Angular now with TS it feels even more Java’ish than ever. TS is great, is specially good for people coming from different languages in that it gives them a more familiar environment but the downside is that they have to make a lower effort to break away from old patterns and truly understand the new platform they’re working on.

AS3 was a huge improvement over AS2. However Typescript type system is incredibly dynamic and malleable yet quite strict.

Typescript gets its huge strength from being able to bring a slider of type safety from pure js - Wild West crazy to strong null checked safety.

Having that pragmatic option of a slider is great for productivity.

> if you look at Angular now with TS it feels even more Java’ish than ever.

This is true, but I think it's an Angular thing rather than a TS thing.

I think it is hurting Angular in the marketplace, too.

Whenever I feel in the mood to troll people, I tell them that TS is just a worse AS3. AS3 has types, classes... it even had XML literals! Eat that, JSX.

To be more precise, TS is a worse ES4. AS was a partially conforming implementation of ES4 drafts that most are aware of. But Microsoft had its own thing along these lines - JScript.NET (in fact, it still ships as part of the .NET Framework, even though it's deprecated).

I've heard it said that Javascript and C are both good languages to compile to -- the language itself is very "fast" in basic operations (V8 is hyper-optimized, C is relatively close to the metal), but an ugly language with lots of gotchas and warts that make it hard for a human to use effectively, but which don't pose a problem for a well-designed compiler.

No lol. Javascript is not a good language to compile to. People compile to JavaScript only because it's the only language to run in browsers.

Most compilers, after type checking and semantic analyses, basically generate a control flow graph with basic blocks. Javascript doesn't have goto so it doesn't allow you to express those low-level things natively, which is why emscripten has to include a relooper component to turn them back into loops and conditionals.

If you want a good language to compile to, look at LLVM. It's a language designed to be compiled to, and it's mind-blowingly easy to compile to than either C or JavaScript.

It's maybe worth noting here that compiling to C and transpiling to JS are two entirely different paradigms of linking and building a software release.

> Aside for some script things for which Python is still a blessing, …

Have you tried using type hints (https://www.python.org/dev/peps/pep-0484/) with mypy?

> Though TS is an MS project, I suggest it really is quite different

I'd be willing to say it's actually the "heart" of the new MS. Eliminating the incentive to selling OS licenses is doing marvels for them.

I'm starting to think Microsoft's strategy with things like VS Code, Monaco editor, and Typescript is "blow the developer away with how amazingly productive they are so that they will want to use more of our products". Because it's just so high quality yet also gratis/libre (MIT license), that I can't see what else they could be trying to do.

Honestly I also think its a recruiting tool for Microsoft. Developers watch the meaningful and useful open source contributions made by Microsoft, and some of the highbrow disinterest in Microsoft due to choices made in the past will fade.

Never forget "Embrace, extend, extinguish". Microsoft has a history of adopting open standards, adding value to them to gain market share, then leveraging that to destroy their competition.

Microsoft might seem to be an "open source champion" now, but remember that it is a publicly traded corporation with a fiduciary responsibility to maximize profits for its shareholders and a history of anti-competitive, anti-consumer practices.

Right now they are being warm and fuzzy to regain the developer mind-share they have lost over the past couple decades to open-source software. If they get a large percentage of developers using their tools and services (looking at you GitHub) I would not trust that they will continue to be so warm and fuzzy.

I know how "Embrace, extend, extinguish" works when the product people are "locked" into is closed source and propriety. But explain to me how this works when the product is open source and can be forked by anyone?

They may not be able to do "Embrace, extend, extinguish" in the exact same way with their open-source efforts, but that doesn't mean they would not be able to apply leverage given sufficient market share.

Imagine if VSCode becomes the defacto code editor. Then they add seamless integration with GitHub, including value-added features which are not available from other providers like gitlab. Developers accept it because everyone uses GitHub anyway. Then they add features to GitHub to integrate seamlessly with Azure, and bug tickets related to GitHub plugins for interop with AWS start to take longer and longer to be resolved. Then maybe one day they change the terms of service, and it's no longer allowed to develop GitHub plugins which compete with Azure. Then they release an update to VSCode which uses deep learning for code completion. On Windows it uses a new DX12-powered subsystem which makes it fast and responsive, while on Mac and Linux it falls back to a single-threaded solution which makes the whole application feel laggy.

In a scenario like that, it would be in principal possible to fork VSCode and make a more platorm-agnostic version, but how realistic is it that an individual, or even a small team will be able to keep up with a large corporation which seeks to make such an effort less successful.

Open source software is great, but if Microsoft owns the governance of those projects, and the up-streams and down-streams, they still have a lot of power over them.

Tooling is commodity now. If they don't make tools that run well on other platforms then other vendors step in to fill the demand, or they just wont have the demand in the first place and they lose mindshare anyway.

This is what happened with Java, Go and Javascript growing rapidly while .NET took a long time to get out of legacy/desktop phase, partly because the tooling was fantastic but isolated to their own Windows platform. They've learned their lesson and have realized there are better businesses by providing the compute and making it easy to use with free and plentiful tools.

Your scenario sounds not only plausible but highly likely. Especially the point about individuals or other principle-driven organizations being relatively unable to reproduce the closed-source advantages that they could tie into open source projects to make them significantly more useful on MS-blessed platforms and systems. This is all a very real possibility.

One assumes Amazon & Google both see that possibility and are developing some in-house expertise on those projects.

But maybe that history is also a strength. The typescript developers never extend the capabilites of the language, just add types. The only exception being decorators, that was a deal with angular so they don't have to invent their own language for angular2 and can build dependency injection on it by typescript exposing the types to the runtime. But decorators are stil buried behind an experimental flag.

You've explained some of the strengths of TypeScript, but I fail to see the argument for how this is evidence that it's somehow a good thing that Microsoft has a history of being quite a bad actor in the software industry.

TypeScript has other features such as Enums that don't exist in JavaScript but aren't pure type annotations either. It transforms into very clever bidirectional map tables.

Yes, and I will start looking into azure because of this same amazing feeling I'm having with VS code and typescript.

Definitely look into it, but it isn't nearly as robust as AWS. Automation/DevOps and data streaming/processing are some lacking areas.

Fine if you just need PaaS app + RDB though.

The interface is astonishingly Windows'ish with millions of knobs to turn and not as stable as you want. (Resource usage metrics returned errors for a while on me.)

You will need time to get used to it. I even use AWS for a Windows instance.

If it's as cheap and powerful as AWS then I'm on board too. But I doubt it is, Amazon's got it down to an intricate science that seems like it can't be beat.

Depending on specifics (obviously), Azure is quite competitive with Amazon on pricing, and extremely competitive on "power". Your mileage will of course vary, but the interesting thing about Azure versus AWS is (surprising to some) Azure tends to use more "open standards" versus AWS' proprietary solutions. (Two examples off the top of my head: Azure supports Docker and Kubernetes directly rather than the Elastic TLAs' own in house container models and container orchestrators. Azure's most used in-memory cache is Redis.)

This is not new -- Steve Ballmer understood this viscerally (ahem) over 10 years ago:


Granted, they weren't releasing much open source then, but it was all free to use, and the quality was far above open source alternatives (e.g. think the Visual Studio debugger vs. GDB wrappers).

I think they just naturally figured out that there's zero advantage to keeping developer tools closed source.

MS has long been about "Developers! Developers! Developers!" But these days they're about more than just Windows developers shipping native closed source applications.

Yet Visual Studio (not the Code) is still a payware with multiple editions.

You can build anything with the free community edition and they keep down-shifting previously enterprise features with every major version.

It's probably multi-faceted. Part of me thinks it's just something they use internally extensively and that branching out to the public means they can easily hire talent to work on their own projects. It's also probably an effort to stay relevant and make amends with web developers, who have long held them in low regard.

And yeah, Azure is a booming business for them. VSCode has a bunch of first-party extensions that reduce friction for working on their platform.

Microsoft's tooling is fantastic

Trying to work with other databases after using SQL Studio is incredibly frustrating

>I can't see what else they could be trying to do.


Microsoft does the same thing over and over - "embrace" a technology, "extend" it by adding some extra stuff to it that happens to work best on Windows then "extinguish" the tech once people are locked in.

Also see: Internet Explorer or Kerberos or Office document interoperability or AIM (messenger) or half a dozen other things.

It appears the tide is changing and what was once hated upon by many in the JS and front-end community, has now become cool and a tonne of projects are migrating. Another project migrating to TypeScript is the Aurelia Javascript Framework for its next version coming out later in 2019.

It's funny because I remember when Angular 2 was announced and that it would be written completely in TypeScript, they copped a lot of backlash for it, but it appears to have been a good move.

I exclusively have been writing in TypeScript for about 3 years now and it's crazy how good it actually is, especially for distributed teams in different time zones. The code is self-documenting and the number of silly mistakes being committed into our codebase has dramatically been reduced, combined with solid unit tests, we haven't really had a code level bug in what feels like months, browser bugs on the other hand...

My favourite thing about TS besides the types and interfaces is the compiler. I no longer have to use transpilers like Babel anymore because TypeScript handles compiling to many different module formats and targets. Back when I used Babel, it felt like pulling teeth because of the different plugins and packages you had to install and configure to use.

I find it really hard to use anything other than TypeScript now, it is simply too good.

I'd argue that porting from JS to TS is a very different beast than porting from Flow to TS.

I've ported code from JS to Flow and could say the benefits are similar to those of porting from JS to TS.

The most relevant part in this article IMHO is the conclusion:

> things like a strong community and availability of type definitions are more important because weak type inference can be solved by "handholding" the type checker a bit more.

I think it's important to highlight that this may be true of his project but not necessarily yours or mine.

Flow's type inference provides a much deeper level of confidence than TS with far less effort, for example, typing only the public interface of a module is often enough to surface type mismatch errors several functions deep within the module. In TS, you'd have to type every function signature to get the same level of confidence.

What worries me about flow is reflected in the comment from the facebook engineer linked from the article: it seems the flow team is focusing heavily on facebook-scale performance at the expense of most every other aspect of the project. A while back, the existential operator was quietly deprecated in the name of perf, and lately some updates have been of questionable soundness, e.g.

https://flow.org/try/#0GYVwdgxgLglg9mABAdxFAFAQwFyIPIBGAVgKb... (compared to http://www.typescriptlang.org/play/#src=function%20wut(a%3A%... )

> In TS, you'd have to type every function signature to get the same level of confidence.

I don't disagree, but isn't this the point? That by typing every function you can reliably have confidence in parameter types and return value types, and if your program is able to throw a type error on build, then it should and alert the developer that they're not logically correct in their implementation.

My point is mostly that you can get away with explicitly typing less things in flow and still get similar levels of coverage. For example, consider this example:

    export function formatTime(time: Date) {
      return digitize(time.getHours()) + ':' + digitize(time.getMinutes());
    function digitize(n) {
      return ('0' + n.toStirng()).substring(-2);
Just a single type declaration is enough to let Flow catch the typo here. In TS, you'd also have to write the argument types for `digitize`. Importantly, if you wanted to explicitly add arg types for `digitize` w/ Flow, your editor can tell you what they are supposed to be.

FLow seem to be better at inferring types. example:

    function foo(n) {
          return n*2;

Object seem to be any object (any type) in both Flow and TypeScript, if you change it to string, both Flow and TypeScript will give an error.

> Even if I have been writing code with Java / C# for nearly a decade , nothing has come close to Typescript in terms of productivity,flexibility and confidence.

TypeScript is, by far, my favorite type system. I love structural typing. I love conditional types (ReturnType<T>). I love things like `keyof T`. I love how good it's inference system is. I love how I can still use it in a JS file and still get type checking with JSDoc.

I tried out Dart over the holidays and it felt like a major step backwards. Seriously, hats off to the developers.

> if I have been writing code with Java / C# for nearly a decade , nothing has come close to Typescript in terms of productivity,flexibility and confidence

I can completely relate. To me the greatest thing is the tooling around it. Using tide[0] on even plain js codebase is just amazing.

I really wish ruby had a TypeRuby compiler (to regular ruby). I know about crystal, but I still want regular Ruby, just with a type checker at compile time, not runtime.

[0]: https://github.com/ananthakumaran/tide

> It's well know that if you start using C# , your entire stack will generally be MS based...(SQL Server, Azure etc... )

That’s only because of Microsoft fanboys nowadays, .NET core is a pleasure to use on Linux and AWS with a variety of databases etc

Yup, it's ridiculous cargo cultism from mediocre C# programmers. There's nothing about C# that forces you into MS's ecosystem.

In 2014, well before Microsoft's dotnetcore oss adventure, I ran the dev team at a startup. Our entire backend was C#. Our devs used the OS of their preference, and so we had Mac, Windows and Linux. Xamarin Studio is (was?) a remarkably decent IDE for something so niche.

We hosted it in Docker (bad call, it was way too new then) on Mono (great call, it Just Worked), on Linux. Data in Postgres. All of this was as easy as doing the same with eg JS or Java (and arguably easier than eg Ruby because C# has a proper cross platform dependency story).

There's nothing about C# that forces you to use SQL Server or Azure. Absolutely nothing.

If you did js->ts, that's not "exact same job", is it? He describes flow->ts and highlights differences between the two.

> It's well know that if you start using C# , your entire stack will generally be MS based...(SQL Server, Azure etc... )

This is very not true. For the past 2 years or so of using C# I never used MSSQL, Azure or even Windows.

> It's well know that if you start using C# , your entire stack will generally be MS based...(SQL Server, Azure etc... )

It may be "well know" but it's entirely untrue.

That's awesome! Did you convert from Flow or just plain JS? I think the experience is much different when converting from Flow vs converting from JS. When coming from pure JavaScript, you do indeed become much more confident and flexible around the code. When you come from Flow, the differences are much more subtle as I described in the article.

Yup. I will never start a new project in plain JS ever again.

To be fair, pure js with types as jsdoc comments that typescript handles just fine is a great way to write code.

No transpiration needed for node modules. No source maps, it’s same old plain JS that’s nicely checked by typescript.

I went from jQuery (inc libs) and < ES5, to VueJS (components) and ES6.

It was such a big jump, after 8 months of it now. I am so much more productive than I was before. It's just not funny.

If I did the same as you. Going from JS to TS, what kind of experience (from there on in) should I be looking at?

IMHO the largest difference is the amount of time you save hunting down stupid issues. e.g. no more spending 3 hours fixing a typo.

Also, code is much more manageable, and refactoring is tons easier.

With enough Babel plugins, you can get ESNext functionality that does whatever you want (some of those early stage proposals are really cool! They might go away tomorrow, but hey, so cool!), so Typescript's old advantage of offering more language features is kinda nullified.

In the end, I started writing the later part of my current project in Typescript, and I much more enjoy working on that part of the code than the earlier parts.

TS takes one day to get going, which is one of the best things about it.

Stick to the 'basics' of TS, which are super general programming syntax structures and idioms - there's really nothing new you'll come across (maybe unions?) - so it's easy.

Then you can try some of the trickier things but frankly we don't use them that much.

You'll be up and running pretty quickly because you can mix your 'new' TS modules with old-school JS code no problem.

And then go from there.

I like TS because it's not some big new fancy paradigm shift: it's just typing, some other pragmatic things, and it works pretty well, very quickly.

It's the only tech I will actively evangelize as being 'the thing you need if you use JS' type thing.

> It's well know that if you start using C# , your entire stack will generally be MS based...(SQL Server, Azure etc... )

Why the fuck...?

I love using Linux+PostgreSQL with F#, and deploying to AWS.

> Even if I have been writing code with Java / C# for nearly a decade , nothing has come close to Typescript in terms of productivity,flexibility and confidence.

Disagree. After doing JS & Java & C# & Perl & F# & TS (& a bit of others such as C, C++, Python), I think TS is just a giant patch to a language that is broken by design. And a patch is just a patch. Just fucking migrate to a decent language already! (e.g. F# or Rust)

From my point of view, they have just great integration together, but but nothing really forces you to use them. Original post sounded like you are being forced into it.

They wrote: "The TypeScript compiler could theoretically perform bundle optimizations based on types and you are missing on that by having a separate emitter and type checker."

Actually, this isn't true, and it's a fundamental property of TypeScript. It's actually the secret to being a TypeScript wizard, and the more you internalize it, the more questions about TypeScript you can answer in advance. I go through some others here: http://neugierig.org/software/blog/2016/04/typescript-types....

It's an important property in part for exactly the reasons this author worries about it -- it preserves the property always safe to type check and emit separately.

It's so fundamental to TS that their bug tracker, when reporting a bug, has a checkbox about whether your request violates this property, and if so they will discard your bug.

It's really the most important feature/aspect of TypeScript -- the fact that it's just a type-checking veneer on top of JavaScript.

It's not its own language like Dart or the myriad of languages (Reason, PureScript, etc.) that transpiled to JavaScript. It's not just that its types are erased at runtime, as you point out; there are many nuances caused by the fact that it's a superset of JS.

While I think TypeScript is the best we have right now, I'm also a bit sad that we need to endure this kind of compromise. Maybe once Wasm because widely supported we can finally cast away the JS legacy and begin anew.

Author here, I actually addressed this in a footnote linked directly to that sentence you quoted.

EDIT: This is incorrect ^, I misread your comment at first.

Thank you for bringing this up, this makes me feel even better about using Babel to emit code.

It was a great post, sorry to only have written a criticism!

I vaguely recall there was some issue with the Babel emit of enums, but I can't remember it well enough to even remember whether it was fixed or not.

I might be missing something, but that sentence does not appear to have a footnote, nor do any of the footnotes directly address the OP's comment.

Oops, you're right, I misunderstood the original comment.

Even though I'm a huge TypeScript fan, I'm grateful for the existence of Flow.

Their focus on soundness has shown the TypeScript team that they should give a lot more priority to getting the typesystem in a better shape over the years. If Flow didn't exist, I doubt we'd be seeing many of the features in TypeScript's --strict mode.

Plus, they were the first to develop some features like intersection types, or even some of the advanced type transformations. Shame it was (at least historically) a bit difficult to find them in the docs.

I'm hoping Flow can push TS to close up the remaining soundness gaps, and fix up some of their features (sealed vs open types is much better than the strange object literal checks in TS)

Competition, even for open source projects is a great boon.

I really would hate for Facebook to kill off flow because Typescript is popular.

Microsoft, as great as they are at OSS, they still have to answer to a chain of Managers who are distant from the actual users of their software.

It's amazing to me watching Typescript and Flow and the choices they make.

From an actual typing standpoint, I MUCH prefer Flow. The soundness guarantees were traditionally much nicer, and the inference it does made some things MUCH simpler (I was able to create a typedef for the react-redux connect function that made it so you could use an entirely un-typed `mapStateToProps` function, and it would infer the types from the reducers as the prop types automatically. It was amazing reducing the amount of code/types needed for each component, and it still gave us intellisense-style hover tips in our editors for what the actual types of each prop were.).

But at the same time, the tooling in the Flow world has always been MUCH worse. I found countless bugs on my own just with what I felt was pretty normal usage, there were many workarounds and issues on windows that were needed, flow-typed is an embarrassment compared to the @types org that typescript uses now, and there are so many weird options and settings that you can use in a `.flowconfig` file that are completely undocumented, deprecated but not documented somewhere, or are facebook-internal or meant to be used with metro (the react-native bundler) only or something.

I love flow, and I still greatly prefer the model they use over that of Typescripts in many ways, but at the end of the day Typescript is winning the tooling fight, and that tooling is bringing many people over. "Imperfect" types that work consistently are better than "perfect" types that are flakey or difficult to use because of the tooling.

I actually think TS team is much closer to the community than Flow's. Just look at the number of open and closed Issues on GitHub.

To me, Flow feels like something they needed in Facebook and they happened to open source. Their main focus is still FB's internal use and the direction is set by it.

TypeScript, on the other hand, does not feel like this at all. They truly put the community first, or so I - and many others - feel like.

> Flow feels like something they needed in Facebook and they happened to open source

This is how most Facebook tools feel to me. It makes me afraid to use them. React is possibly an exception, but again -- too afraid to use it because Facebook's open-source offerings seemed to have only selfish goals in the past and often seem to be full of important bugs.

> Microsoft, as great as they are at OSS

Just a few years ago, that would have been a laugh. Don’t get me wrong, I love the world we’re living in :)

Are there any people that have switched to TypeScript that still think dynamically typed languages have advantages over statically typed ones? Even for small projects with only me working on them, I find static typing saves me a ton of time and dynamic features are very rarely required or useful. The vast majority of bugs in my TypeScript programs collect around the places they interface with untyped JavaScript code.

I bounce back and forth between TypeScript and Clojure. I've been keeping a close eye on Clojure's upcoming spec as it seems to be a nice opt-in, run-time type-checking solution. I like the flexibility and quickness with which dynamic languages let me iterate, but I want the safety. I want at least some of the benefits of both.

So yes, they (dynamically typed languages) have some advantages. Sometimes when I'm iterating quickly, especially on new projects, I don't want to fight with the compiler. BUT, I vastly prefer TypeScript to JavaScript because JS has a lot of foot-guns. I think when we get the existential operator, it will have less foot-guns, but I will probably never again write a new project for work in JS from scratch.

I really love the ideas Rich Hickey put forth in his most recent Clojure conj talk: https://www.youtube.com/watch?v=YR5WdGrpoug

The ability to spec out the shape of an object, and then in the different contexts in which you need some of that data, specify what you need, is an awesome solution to optionality (Watch the talk!). I absolutely love how much thought RH has put into the design of Clojure, and I think the final release of spec will be a close to perfect balance of dynamism and opt-in safety.

Of course, TS is a superset of JS, so adopting it little by little sorta gets me that balance I want.

> I don't want to fight with the compiler.

The only time you're ever fighting the compiler in a modern statically typed languages with type inference is when you've made a mistake.

Or you're trying to do something fancy that the compiler/type system doesn't support

If you're doing something so "fancy" that the compiler/type system doesn't support it, you should probably re-think it.

Nah, there are still some patterns, especially around metaprogramming, that TypeScript doesn't support. One example is the inability to refer to the current class inside a static method. This makes it really awkward to write things like factory methods, which in turn forces you to either resort to copy & paste coding, or fighting with the compiler. See https://github.com/Microsoft/TypeScript/issues/5863 for example.

Higher kinded types are currently not supported by the TS compiler and would be a highly appreciated feature.

Perhaps, but if that means doing it in a more cumbersome way, or with mire boilerplate, then that would be an area where dynamic languages have an advantage.

IMO, one of the geeat things about TS is that you can opt out of the type system for specific functions if you really want to.

Is it really the compiler/type system's fault, though? Sounds more like a language's design or specification flaw.

> The ability to spec out the shape of an object, and then in the different contexts in which you need some of that data, specify what you need, is an awesome solution to optionality

You realise you can do the same thing in TS with its structural type system?

Regarding Typescript specifically, I'm a bit torn.

I used to be excited about it but after a while of using it I see it more like a tool to migrate existing JS code or to interface with existing libraries written in JS, and not like an ideal tool to write code from scratch.

The reason is, the type system is actually incredibly complicated (to accommodate to all sorts of silly and messy things you can actually do in JavaScript).

I'd prefer using a more principled language for brand new projects. Which language is that? I'm not sure yet, but dynamic vs typed doesn't seem as important to me. I think ClojureScript is a better language than TS. Kotlin/JS also looks good, for a less "alien" language that is still better typed than JS/TS. But I don't have experience working on medium/big projects with these yet.

OTOH, for browser apps, the main plus for TypeScript is that if you pick a subset, ala C++, you get an OK language that never gets too far away from what you actually get when you convert the app to JS, so easier to debug and sometimes reason about.

So there you have it... I'm torn :-)

> The reason is, the type system is actually incredibly complicated (to accommodate to all sorts of silly and messy things you can actually do in JavaScript).

Can you give some examples?

I have a feeling if you need that, you are writing some complicated magic code, but I might be wrong.

Yeah, if you read release notes for 2.8 [1], pretty much all of the features explained are things you probably don't want to use for every-day code. I'm pretty sure a lot of those additions were done for supporting different things in existing frameworks like React and others.

The problem is, as soon as you provide those features, people will want to use them to add "type safety" to code that could otherwise be very simple. Sometimes simplicity is a better approach. This is why I say you will probably want to pick a subset of TS and leave the advanced types for interacting with legacy code or 3rd party libraries.

Also, I'll leave this link here :-) [2]

1: https://www.typescriptlang.org/docs/handbook/release-notes/t...

2: https://github.com/Microsoft/TypeScript/issues/14833

Here's a couple specific examples I just saw within the last couple days.

`immer` is an immutable update library based on ES6 proxies. In addition to safely applying "mutative" code immutably, it also freezes the output to keep you from accidentally mutating it elsewhere.

An Immer maintainer just added some kind of a `DeepReadOnly` tag to its typings in a patch release, which broke things for a bunch of people:


This specifically broke our PR to convert `redux-starter-kit` to TS, with this lovely error message:


I'm not saying every use of TS is that complex, but I do absolutely think that TS types can get ridiculously complex (and that people can get sucked into spending _way_ too much time trying to model dynamic JS behavior in static types).

Does TypeScript let you build a program interactively as a living thing? Example of what dynamic languages for the browser (ClojureScript) have been doing for 5 years now: https://www.youtube.com/watch?v=KZjFVdU8VLI Given that frontend work is so often an endless series of tweaks I don't know how people stand doing anything big for a long duration the classic way of edit -> compile (refresh) -> rebuild runtime state to look at what changed/play around/test -> repeat.

Typescript can be indeed run directly inside the browser, though that's often not recommended, and none of the currently recommended "Hot Module Reloading" tools that I know of take that approach.

Most of the HMR tools out there support Typescript in some way directly or indirectly (Typescript plugins for webpack, etc).

`tsc --watch` is quite handy and useful in those situations where an HMR tool doesn't support Typescript or you aren't using an HMR tool at all. (For instance, on one project recently I've been just using npm package `lite-server` which implements "BrowserSync" automatic reload and `tsc --watch`, because I currently don't need a full bundler like webpack, though I expect that to change soon.)

To me that example doesn't look all that different from typical hot-reload provided by webpack or similar. Can you tell me what I'm missing here?

I'm not familiar with webpack but in general hot reloading isn't quite the same as dynamic redefinition, but can be close enough for many purposes. If you can set up something approaching Java's hotswap (made even better by JRebel) then perhaps the advantage is lessened.

You get dynamic redefinition with Wright[1][2] and chrome, and you can plug in Typescript pretty easily.

In v2 currently under way, you can use es modules which gives you a much better experience since you can reload a single file instead of an entire bundle (always sub 100ms update times).

[1] https://github.com/porsager/wright [2] http://porsager.com/wright/example.mov

Over the past year I converted 50k loc language beautifier to Typescript. The Typescript compile time is about 5 seconds give or take half a second, which is about 99% of my total build time.

I have a tool I developed that is a combination of a web server and web sockets that automatically rebuild and refresh the browser when a code file is saved.

Is this a full build? Have you used `tsc --watch` at all?

Honestly I wasn't even aware of --watch or I had forgotten about it. It doesn't do what I need though.

Updates to both .ts and .css files trigger the build. The build step compiles the Typescript files into JavaScript and then merges certain JavaScript files together and makes minor modifications on the fly. I prefer to do testing in the browser as its easier for me to read the code output there, so once the code is properly packaged web sockets fires a notification to the browser to refresh the page which brings in the freshly built code.

My raw TS and CSS code exists in several different files for management, but the final packaged code provides the browser 1 CSS file and 2 JavaScript files, which decreases the HTML load time.

If build time is an issue (not sure if it is or not), you can have `tsc --watch` running for the `.ts -> .js`, then also have a separate watcher for `.js` and `.css` changes that builds your final bundle.

the problem with most static typed languages is that the type systems are just not very flexible. Typescript union types are 10x more useful than Haskell's `Either` type, and you can build extremely complex type signatures that let you build clean APIs just from that.

Static typing is nice when the tools given actually let you build nice APIs

Good thing about TS is that it's currently an evolving language and if you have problems with the typing system, there's a good chance it will be improved instead of other languages where it might not pick the improvement up quickly which makes you feel like the language is at a dead end.

Small population that would expend the effort for something expected to be of little benefit at best.

Some people can’t seem to function without static (global?) types though, and this works for them.

I use Flow at work, and have plenty of experience with both statically-typed and dynamically-typed languages. I think it's a simple tradeoff- more certainty for more toil.

It can be a real chore convincing Flow that working code works, and this can force you to use less-readable idioms to make the typechecker happy. It also lards up your code with extra dollar signs, angle brackets, and other crud, making it less readable.

These can be acceptable costs depending on your project, particularly the size of it (in lines of code, components, number of engineers, however you care to count).

This is wrong: (from the article)

  function f(leaves: Array<Leaf>, aggregators: Array<Aggregator>): Array<MemsqlNode> {
    // The next line errors because you cannot concat aggregators to leaves.
    return leaves.concat(aggregators);
That's just wrong, of course you can't concat aggregators and leaves! and Typescript is OK to not accept it. If you want that to be ok, you could do something like

  [].concat(leaves, aggregators)

I like his take at the end:

> We converted a codebase that was adapted to Flow to TypeScript. This means that we obviously only found things that Flow can express but TypeScript can't. If the port had been the other way around, I'm sure we would have found things that TypeScript can infer/express better than Flow.

So he's aware of the conundrum, and, I hope, his readers too.

Concat doesn't mutate the original array, so there is no reason the output has to have the same value as the original array. Typescript has chosen to implement it this way, but it's not a limitation of the underlying JS method. Saying it's wrong because it's how Typescript has typed the function is circular reasoning.

Consider the same functionality exposed as a function, it would seem be fine to type this as:

   function concat(a: Array<A>, b: Array<B>): Array<A | B> { }

Also, you may consider

    [...leaves, ...aggregators]

Author here. I understand this example may be a little controversial. However, from my point of view, if I annotate my function as returning an `Array<MemsqlNode>` and `leaves.concat(aggregators)` can be cast to `Array<MemsqlNode>`, then I don't see why I shouldn't be able to return this in this function without verbose syntax or "tricks".

it's not a trick. Yes, it can be casted, but you should be explicit about it. AFAIK, the signature for the concat function is dependent on the array you're using it, so in this case, it's Array<Leaves>. Even though concat returns a new array, it is expecting the arguments to be of similar type. That's a "safe" behavior and something I would expect. Auto-casting to the return type (in this case) is something that I would not expect to happen, as it could be the source of errors.

Doing [].concat(Array<A>, Array<B>) is fine, because you did not specify the type of the array for the literal [], it's not a trick, in that case the type should be Array<any>, but it's casted to Array<MemsqlNode>.

Right but isn't that effectively changing the runtime code (thus making it a trick) in order to fix a type error? I don't know if the performance of `a.concat(b)` is the same as `[].concat(a, b)`.

It's not a type error. I think the error is in your definition of a function and types like that. I would've defined your types like in this example[0]. However, I don't know enough about your codebase or why you're doing this.

[0]: http://www.typescriptlang.org/play/#src=type%20NodeType%20%3...

The argument is that you have a potential type error in `a.concat(b)` that you hadn't considered before in that code: If `a` is defined everywhere as Array<Leaf> and you push() something to it that is not a Leaf you potentially broke one of your own invariants somewhere else that uses `a`. In JS concat happens in place and is a mass push, so the same general concept holds.

`concat` doesn't happen in place, it builds a new array:


True, but does not mean the type of the array that should be built should be of different types. I think the sane definition by Typescript is that it should be of the same type: that's why it's an instance method and not a static one. If this was a static method (Array.concat(a, b)), I would agree that would be a good assumption, that the type of the resulting array should be union of the types of the arguments.

Interesting. Then that should be a fixable bug in TS' lib files then.

There's an issue mentioning it with a PR Welcome sign up: https://github.com/Microsoft/TypeScript/issues/27545

Ah, and there is at least one PR specifically for it: https://github.com/Microsoft/TypeScript/pull/25716

Looking at the type definition, it is expecting the same type:

      * Combines two or more arrays.                                                                                                                                                                                                                                                                                          
      * @param items Additional items to add to the end of array1.                                                                                                                                                                                                                                                            
    concat(...items: ConcatArray<T>[]): T[];                                                                                                                                                                                                                                                                                  
      * Combines two or more arrays.                                                                                                                                                                                                                                                                                          
      * @param items Additional items to add to the end of array1.                                                                                                                                                                                                                                                            
    concat(...items: (T | ConcatArray<T>)[]): T[];

That's typescript's type definition.

Flow recognizes that concat returns a new array, so it specifies that calling concat on an Array<T> with an Array<S> as an argument returns an Array<T|S>. Their actual definition is:

    declare class $ReadOnlyArray<+T> {
        // concat creates a new array
        concat<S, Item: $ReadOnlyArray<S> | S>(...items: Array<Item>): Array<T | S>;
Personally, I'm ok with that and think it is useful. Although the return value could be an array of any type more general than T|S and the types of the argument arrays could vary. (i.e. Array<T>.concat(Array<S1>,Array<S2>,...): Array<? extends (T|S1|S2)>) but I don't know if that can be expressed in flow.

I don't think either gets it perfect, but flow is trying to at least capture the fact that the return value can't be an array of a type that is disjoint from T|S.

In practice, I've been impressed by the level of detail of information that is captured and propagated by flow's type checker.

If the return type was Array<Leaf | Aggregator>, wouldn't TypeScript be able to infer the right type U in Array<T>.concat => Array<U> ?

No, because JS concat is an in place operation, Typescript models it as Array<T>.concat() (intentionally leaves it "narrow") rather than Array<T>.concat<U extends T>(). In this case it is the input type that potentially would need to change so that the left hand side was Array<Leaf | Aggregator>.

Or as others point out, array spread does widen unlike concat (because it isn't an in-place operation).

Apparently concat does not operate in place, my mistake. I've been coding in TS for so long I assumed its definition was correct.



What about the speed of type-checking? Flow is near-instant by keeping everything live from the last check whereas TypeScript has always been much slower.

Other than that aspect (which is very important!) I do think TypeScript is likely a better choice if you use any third party libraries that already have definitions available.

I work on a >100KLOC Flow codebase and I can tell you the type checker is anything but instant.

I work exclusively in VS Code with TypeScript. It is very fast after it first parses the project which isn't instant, but I often use the AWS SDK and some other packages with very large type surfaces. I'm not sure how fine grain it gets with caching, but it feels like it at most only ever needs to re-evaluate the file you are editing and this is practically unnoticeable to me.

> unfortunately when you share a TypeScript playground link, those settings are not saved in the URL. For this reason, you have to manually set them when you open any TypeScript playground link from this article.

There is a much better typescript playground[0] that supports all options in the permalink, for example see this link[1] that has `removeComments` enabled.

[0]: https://typescript-play.js.org

[1]: https://typescript-play.js.org/?removeComments=true#code/PTA...

There's also a good TypeScript playground clone here as well:


This links to the same code as my post. You might just have an older bookmark. See this PR[0].

[0]: https://github.com/agentcooper/typescript-play/pull/24

I wish Typescript had support for partial function application, since I use that much more than “Executioner” classes.

Actually, I’ve moved away from OOP in general in my JavaScript, only as a last resort. (Not going to put in long comment from tablet screen...)

Outside of type annotations, Typescript never supports syntax that isn't in an ECMAScript standard or a popular proposal.

That said, there is a Stage 1 TC39 proposal for partial application. https://github.com/tc39/proposal-partial-application

Thanks for the tip.

I have been using Ramda’s R.partial() function a bit, as well as some in-house stuff (e.g. - to generate 0-arity event handlers), but this would be a very helpful feature, particularly once IDE recognition becomes widespread.

Background: suite of related projects started in early 2015, ES5 on IE11 kind of limitations... (recently started using a transpiler for syntactic sugar)

Partial function application would be sweet. I wind up manually currying a lot of functions and readability starts to get sketchy.

  const foo = (a: number, b: number): number => { /*...*/ }

  const foo = (a: number) => (b: number): number => { /*...*/ }

I'm currently porting about 15k lines of my own JS to TS. There's something so satisfying about finding all kinds of bugs through this process.

And what I love the most: anywhere that I absolutely positively don't want to deal with it yet: `any`

> Lower quality editor/IDE integrations (when compared to TypeScript)

This is one of my favorite aspects of TypeScript, the first-class support it receives in Monaco Editor (part of VS Code) which is one of the reasons I also chose first-class TypeScript support when making Autumn, since I could embed Monaco right into it and get countless amazing IDE features for free.

> unfortunately, we had to change a very small amount of runtime code to fix some logic that TypeScript could not understand

Would be very curious what this is, I've only found maybe one or two things TypeScript could not understand, and they were already under active development in the Github repo.

In our case, we had some "fun" React HOCs which were difficult to type properly in TypeScript. We re-wrote the HOC to be easier to read and maintain, so while it cost effort I consider it a net gain.

> countless amazing IDE features for free.

Is this just from LSP? Or are there extras that are built into VSCode?

Ironically typescript doesn't have a supported language server from MS. TSServer (in the typescript npm package) is language server-esque and that's what VS Code plugs into. I believe the main language server for typescript is maintained by sourcegraph.

VS Code is written in typescript though and the projects have a pretty nice cadence of new features. Typescript is very much a first class citizen in VS Code

Pretty much anything that comes from Microsoft today that is a web app, or uses the HTML for UI on the desktop (such as Electron apps), is likely to be written in TS. Not even because there's some push to do so, but because people who mostly write in C# and C++ find TS far more preferable.

(That, by the way, is one reason why you can trust that it's not going away anytime soon.)

Thanks for sharing that.

We've just started a new Node project where typing was one of the main drivers and went through a similar thought process. We use Flow on our client code but the positive chatter about TypeScript had made us wonder if we should re-think that. We have similar thoughts on a lot of the trade-offs. Flow feels better at inference but the TypeScript ecosystem is amazing.

We ended up sticking with Flow for consistency (and we don't want to migrate the client right now). It's good to know that a migration isn't impossible if we ever change our minds.

Thanks for this writeup! We also chose Flow in 2016 for the exact same reasons you listed.

We aren't planning on switching anytime soon because we also are using Relay which generates flow types for our GraphQL queries - but its good to know its doable if we need to.

Relay supports generating Typescript types for queries as of v1.7.0

[1]: https://github.com/facebook/relay/pull/2293

[2]: https://github.com/relay-tools/relay-compiler-language-types...

For anyone working on Nodejs targeted TypeScript I would highly recommend checking out ts-node. I use it a ton when I want to not worry about the pre-compilation step for various reasons. Can work with absolute imports, and caches code on the first run as well. Pretty sweet.


Article mentions ts-jest; can also recommend. I use Jest for all my TS projects from serverless to selenium. Its watch mode is fantastic.

I also typically use ts-loader with Webpack instead of Babel.

Eh, might as well toss a few more recommendations in there:

* source-map-support

* Bluebird for long stack traces with async/await(v8 now has a zero-cost feature behind a flag now though as of end of 2018)

* Get the debugger hooked up to your browser/nodejs process for a great debug experience

Note for --transpileOnly flag which speeds up compilation if you've already checked for typing errors in your editors and don't need to do it again on compilation.

There's also ts-node-dev which is a variation of node-dev that auto reloads files loaded on change.

As for a bundler, parcel supports TypeScript out of the box which just allows you to write browser side code with TS without effort.

We use it but it can cause complications with other tools that expect compiled JavaScript such as testing and code coverage. They're solveable but the choice isn't free.

> This number scared us (a lot) at first but we quickly figured out that most of these were related to TypeScript not supporting .js files.

Typescript actually does support .js files. You just need to use the `--alowJs` option.

I think it's a catastrophically poor decision to not have the allowJs behavior as the default. That one flag fixed almost all of the QOL issues I was having.

It's the default if you name your project-level config file jsconfig.json instead of tsconfig.json, which is an interesting convention versus configuration trade-off. VSCode makes this somewhat clear in some of its tools (it will offer to create a jsconfig or tsconfig for you and tries to guess based on source code metrics), but it's easy to miss in most Typescript documentation.

What are the chances of ecmpascript adopting optional typing like they adopted the features in coffescript?

I've seen a few proposals around but it seemed that they were are stalled.

TC39 still has scars from ES4 which tried to add optional typing (and is the only "lost" ES standard that browsers never implemented).

My hope is that since then we have examples like Python's PEP 3107 [0] that help make sense as where a TC39 proposal could move forward: encourage browsers to AST parse type annotations, and ignore them by default; leaving the actual semantics still to tools like Flow and Typescript until there is a better convergence in semantics and/or browsers have greater needs of specific type annotations.

[0] https://www.python.org/dev/peps/pep-3107/

I've had the same thoughts. Seeing what happened with CS, getting too involved in the TS ecosystem seems like setting yourself up for some future pain if what you're doing will be long-lived.

I actually think we'll see more advances in webassembly first, though. It won't surprise me if Java makes a comeback for the frontend.

It's a really hard thing to spec for runtime.

Lots of love here for TS. I'd like to share my personal experience with it: I went from "fast and convenient" with JS to constantly fighting against the TS compiler and linter above cryptic type errors. Man, was it frustrating and JS felt suddenly crippled because it was hard to use its dynamics. TS catched a mistake here and there - true - but that did not equal out the hours burned by writing and debbugging type structures. In the end I removed every bit of TS from my projects and continued happily without it. I found out that using JSdoc comments and relying on the linting abilities of my IDE plus writing unit tests gave me stable code as well, without pulling my hair out. I was there before TS and just went back to it.

I get that TS may be a good idea if you deal with teams of mediocre devs to prevent they do stupid things but by talking with a couple of JS devs and looking at my own JS experience it seems like the desire of using TS vanishes if you reach a certain experience ans confidence using vanilla JS. It looks like the most people who embrace TS see JS as some kind of inferior thing, anyways - but that might just be a wrong observation by me.

Damn, a sweeping generalisation if ever I've seen one. Are all the people using c++, java, haskell just mediocre devs because they like the safety brought by a type system over lisp or python? Microsoft, Facebook, and Google all wrote either a type system over JS or a JS alternative because of the problems they faced with vanilla JS. Facebook is even doing both ‍️.

Perhaps spending some time looking at how certain libraries write their type definitions will help you learn how to use them, if you're constantly fighting you are probably missing some fundamental concept that TS operates with. TS has also gotten much better at expressing more complex types over time and there are still more things on the roadmap.

For me TS only grows more useful as the size of the codebase increases. It allows safe refactorings you could only dream of in a large JS codebase.

> I went from "fast and convenient" with JS to constantly fighting against the TS compiler and linter above cryptic type errors

Would you, perchance, be able to share an example of such an error? We generally strive to improve error clarity where possible~

> I found out that using JSdoc comments and relying on the linting abilities of my IDE

Ah. Well, spoiler alert: if your IDE is vscode or VS that JSDoc is actually still getting typechecked by TypeScript ;)

Do you have a list of common errors that TypeScript usually will detect ?

> TS may be a good idea if you deal with teams of mediocre devs to prevent they do stupid things

I don't consider myself a mediocre dev but I can still do stupid things and TS tells me before I run it.

> It looks like the most people who embrace TS see JS as some kind of inferior thing

JS does feel inferior once you use TS.

You sound like you're just used to your workflow if you had to pull hairs out by just using TS.

I don't understand how people do refactoring without static typing. If I change a function parameter in Typescript then I get compiler errors for each use. Awesome. With plain Javascript I have to run the program and get it into executing all those different invocations and hope I get meaningful errors at runtime. A nightmare.

In my experience Javascript code bases end up being "add new code only", because changing anything is just too dangerous, because type errors are so hard to find. So after a while of several people working on the code base it just becomes a bloated spaghetti mess that cannot be untangled, because refactoring is nigh impossible.

There are some tricks you can do in order to break up the code base to make it more manageable using the plugin and module patterns. Each part of the code that can be useful in other code bases you can break up into modules. For example a Vector library if you are making a game. It's OK to move out even small functions if they are common enough, like the famous leftpad module. Next step is the plugin pattern, where each plugin is totally independent of other plugins, where the plugins communicate to the main codebase via an API. The third trick is to move complexity to the architecture, meaning that some part(s) of your system, often a bottle-neck that need to be horizontally scaled - is moved out into it's own code-base aka micro-service. Now it will be much easier to refactor, you can for example write the module, plugin or micro-service in another language. I've done my fair share of managing code bases with 10k LOC of spaghetti where a change of one parameter can break code at 100 other places, but thankfully there are better ways. Then there's the fifth trick that is an old JavaScript convention, that if you do change a parameter, you also make it so that you can use the public function as before in order to not break stuff, that's the true power of dynamic JavaScript, for example if your function takes three arguments, and the first two is optional, it should be possible to call the function with only one argument, and the function will figure out what parameter belongs where. Then if there's an error in the parameters, the function should throw early with a nice human readable error, debug data/dump and stack. Also see defensive programming. Then the sixths tip is testing. After a certain point, when it takes longer to manually test, then it takes to code, it's time to introduce automatic testing. It doesn't have to be unit tests. It can for example be regression test, meaning that for every bug you find, you write a test that would automatically have found that bug - before actually writing the bug fix! Then the next step is to write integration tests (not sure about the name), where the code functionality is tested from the outside, for example in a web app a button is auto-clicked, then the DOM state is checked to make sure everything is correct. There are a ton of different types of tests you can apply, for example perf-test that make sure the performance has not degraded.

Changing things to be more modular in order to make it more manageable is a great idea.

But the problem is still that you cannot make any such change to existing large Javascript codebase, because you don't have the kind of tools that you need in order to do so with any confidence that you won't have an enormous amount of uncaught runtime errors when you're done. This is what a static type system gives you.

I agree, the only reason I can see for TypeScript's existence is that people were coding poorly. Many of the mistakes people claim to have made as a result of dynamic typing, I simply did not make because I learned dynamic first, then static second. Also, it's solved by literally 1 test, and people are touting an extra compilation step with errors to solve that problem. This eco-system is a poisoned-well of bloat, and I'll be redoubling my efforts with Rust to leave it as soon as possible.

> plus writing unit tests gave me stable code as well, without pulling my hair out

apart from the generalization i tend to agree

from uncle bob's article [1]:

> My own prediction is that TDD is the deciding factor. You don’t need static type checking if you have 100% unit test coverage. And, as we have repeatedly seen, unit test coverage close to 100% can, and is, being achieved. What’s more, the benefits of that achievement are enormous.

I've worked on high mantainable big JS projects (+40 devs) with 80%-90% test coverage. plus we were using GraphQL so inputs were valid types somehow.

if I start a new project today I would use only these technics (valid inputs + integration test) with no typed lang.

[1] http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.htm...

> A very common function in our source code is the invariant function. I can't explain it any better than the documentation does

Seems like you'd have an easier time explaining what it does if you'd just called it "assert()" in the first place. This isn't a new concept, why does it have a new name?

A function by that name is apparently treated specially for flow. My guess is it interprets it like the if statement OP ended up using, where applies the first condition to the type checking.


It originates from design by contract (but is still the incorrect term). An assertion is not a contract.

How so? The major design by contract language I know of is D, and its contracts are essentially asserts that are either placed at the beginning of the body or prior to all returns.

You can certainly get substantially similar behavior just using asserts at the beginning of the function body and ensuring asserts at the end before a return are hit by all code paths (e.g. make an anonymous function that contains the body, then call it wrapped in a try/catch and assert on the return value.

Unless I'm missing something about design by contract, it's essentially just asserts at the beginning and end of a function.

Precondition (called invariant here): a set of conditions that must be met in order to call a method. Another name for argument validation, but it can be other things (e.g. connected must be true before calling methods on a connection).

Postcondition: guarantees that a method makes. For instance, it won't return null.

Invariants (incorrectly used): conditions that always hold true on a type, e.g. the starting index on a slice must always be greater or equal to zero.


I guess D is guessing via implications, but from what you've said it can't support invariants, meaning it doesn't strictly qualify as DbC.

I'm curious about the matching for the Maybe type (type Maybe<T> = T | void;). Is void an alias or superset of undefined? Perhaps because undefined is a reserved word? Wouldn't it make more sense if it was either:

type Maybe<T> = T | undefined;

x === undefined


type Maybe<T> = T | void;

x === void; // or: void?(x)

assert() is not run in release or optimized builds in any language I'm aware of, while invariant() is in the place I've seen it used. Similar to CHECK() in C++ codebases.

Invariants in design by contract are usually also not run in release/optimized builds.

With asserts, it's not uncommon to provide a separate switch, that defaults to excluding them in release/optimized builds, but you can override. And some languages make the distinction explicit in asserts themselves - e.g. in C# (and any other .NET language that uses the stdlib for this), Debug.Assert will be excluded #if DEBUG, but Trace.Assert will not.

No experience of Flow, and I hear all of the opponents of these "superset JavaScript" solutions who lament their inability to really tighten up the language in a way that a true static language would. However, JavaScript is clearly going nowhere anytime soon, and whilst WASM has the ability to change the state of play, I've found adopting TypeScript in general to be hugely beneficial.

It's 100% fair to point to all of the things it doesn't provide, or indeed the way that it tries to make JS out to be some classical OOP language which it's not, but it's helped me track bugs and refactor code in ways which would never have been possible without it, and for that I'm grateful. In comparison to other tools such as mypy, TypeScript is light-years ahead in its general ease of use and community support.

I migrated Polar from JS to Typescript:


It's a pretty sizable app. The migration wet pretty smooth and I would absolutely never go back. Typescript is amazing.

Polar looks brilliant. Thanks for creating this and making this open source.

Partially inspired by this post (I was already convinced), I finally finished adding typescript to a javascript project I recently inherited. I'm an experienced backend developer but relatively new to frontend javascript stuff. Short term, I need to own this until we can expand our team.

Errors that a compiler would normally save me from when doing Java/Kotlin are a major PITA when you are dealing with a foreign code base in Javascript. Over the past few weeks I've slowly gotten comfortable with the code base, the build system (rollup, babel, less, and a few other things etc.), and the frameworks we use (redom, mainly). After updating everything to their latest versions (e.g. babel 6 -> babel 7) and getting that working; I figured adding typescript should be straightforward.

Turns out this is super easy and delivers instant value. The hardest part was figuring out what part of our somewhat convoluted build.js script to modify so that it processes ts files as well. After finding the relevant for loop and adding the rollup plugin for typescript that seemed to be working out of the box. I added a tsconfig.json file to support some ecmascript2017 stuff we apparently are using.

After that it got easier. I fixed a few hundred lines of javascript code to have types. That's in my first hour of using typescript. VS Code and tslint are very helpful. I also found and fixed the first type bug that slipped through earlier: instant value.

Kudos to MS. Vs Studio Code is a game changer. I've been shying away from doingb frontend stuff for years for reasons of it seeming to be backwards to be coding blindfolded with your hands behind your back. VS Studio Code changes that. Autocomplete, quick fixes, refactorings, import organizing, etc. So much better than my browser telling me I messed up by not loading the page.

I renamed several js files. Worked my way through the clearly marked warnings. Mostly quick fix works reasonably well. You do need to replace the any type with something more appropriate usually (like e.g. string). Anyway, free of warnings and vs studio code also found and added a redom type definition for us. Awesome. Very impressed with how easy that went.

Typescript only for me going forward. Opting out of a perfectly good type system seems silly.

Glad to hear Typescript is winning hearts and minds. They’ve been great maintainers; avoiding the infighting, toxicity, and the perverse incentives that unfortunately plague the front-end landscape.

> We didn't have to buy into an entirely new ecosystem of JavaScript development. Dropping Babel for tsc (TypeScript compiler) was scary because it wouldn't give us the flexibility

This shouldn't really have been an issue. It's always possible to not do any transpilation and leave it to another tool

Ok, well done! BUT.... Now port it to something you can really trust like Reason ML. See https://www.reaktor.com/blog/fear-trust-and-javascript/

Or Purescript =)

Any opinions on Webstorm vs Visual Studio for using TypeScript?

I was a long time Webstorm user until switching to Typescript, and found that its Typescript support is pretty far behind VSCode's. Its been around 6 months since I switched so I can't remember the specifics, except that VSCode generally had better indicators of what was wrong with the type.

Neither is perfect at displaying errors but that might also have something to do with your ts config. I also think that Webstorm is a much better editor than VSCode in every other respect, but for me VSCode is 1% better for my productivity so I am forced to use it.

> found that its Typescript support is pretty far behind VSCode

This isn't (can't be?) true. Both editors use the same language server. I've used both and found their language support to be identical, and WebStorm feels a little more natural to me when using automated refactoring.

I also have both eslint and tslint set up through WebStorm, so I have everything integrated in one place: hinting, linting, compiler warnings, and compiler errors.

VS Code can do the same, as far as I know. I just prefer WebStorm for other reasons.

Wouldn't relying on the language server service yield the same results from checks?

Unless I'm mistaken, one major difference is that Webstorm costs money.

Which isn't "major" if you figure out it's just $5 a month (down to $3 from your 3rd year).

FWIW, It's much more per user for commercial / corporate use. Like, minimum hundreds a year: https://www.jetbrains.com/store/#edition=commercial

Webstorm by itself is $129.00 yr for commercial use. Im replying to myself to make this more specific edit to my above comment in response to dude below that found it worthwhile to find me on twitter to tell me I'm wrong there as well in a Tweet because $129.00 != hundreds, geez! Dang, ban this user again!


What is Typescript providing that you don't get by writing 1 test that checks type and by following modern ECMAScript specs? Does that justify a new syntax? The answer I think is no. I see much praise in this thread, but very little definition of why that praise is merited.

> writing 1 test that checks type

If you enjoy being a human type checker, no-one in stopping you.

Tests don't cover everything and just on input and result. All the code in the middle can still have bugs that are uncaught because you don't have the right test for it.

Type systems provide a lot of safety with less work, meaning less bugs and more productivity. Just having the definitions available when writing makes you go faster.

> Type systems provide a lot of safety with less work, meaning less bugs and more productivity

I hear this argument all the time. As well as the opposite: type systems slow you down and only catch easy-to-find bugs. Would love to see a real study that quantifies productivity among static and dynamic systems and ends this flame-war once and for all.

I dont think there are formal studies but there are quite a few discussions, this one on SE is good: https://softwareengineering.stackexchange.com/questions/5960...

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact