The important parts here are (1) that they remained metrics driven and (2) that they focused on repeatability.
1. Both TypeScript and Flow are gradual type systems. They both explicitly allow escaping the safety and coverage of a type system, either to allow easier interop with existing JavaScript packages or to allow existing JavaScript codebases to incrementally adopt types. So there are always holes in which errors are reported, regardless of whether they had to silence some explicitly or not. By measuring the extent, they have a yardstick against which they can drive further type adoption.
2. Repeatability is the only way to do large code migrations. Write the steps in a script, run it, find the bugs in the script, fix the script, and repeat. With 300k lines of code no doubt they have dozens or hundreds of people committing daily. So a script is the only way to sneak in during off-hours, get something in that doesn’t race to conflict with someone else, and land the change. A long running, manually crafter branch here would not work—it would effectively operate as a fork of the codebase for the entire duration of the migration, with all the downsides that a fork entails.
Kudos to the team; this is a very impressive feat!
I recently underwent a very similar experience at MemSQL[1] but for only 30K LOC. The pain points that led us away from Flow to TypeScript seem to be the same as the ones as Quizlet's .
Overall, I'm very happy to see more and more teams move to TypeScript as a direct consequence of its Babel 7 support. TypeScript makes more sense as a pure type checker and not as a full blown transpiler.
It's also interesting to see that their type coverage increased from 66% to 86% (ours increased from 88% to 96%). This pattern is probably because of better third party type definitions.
I still prefer Flow's Type Inference and philosophy over TypeScript's, but in practice TypeScript is much better pretty much any use case I can think of.
There have been a few posts like this, and the discussion always seems to focus on plain JavaScript vs TypeScript. I'd like to see some talk about Flow vs TypeScript instead. We have what I'd call a medium-sized project (~30k loc) written with Flow and TypeScript's superior tooling is indeed attractive enough that we too have been thinking about making the switch.
Having used both, there are a few features in Flow that I'd miss. I think classes being nominal is a good approach and the ability to declare new nominal types is useful. Otherwise Flow's type system is structural, like TypeScript's. Function parameter type inference is also very nice feature, so I can make simple module- or function-local helper functions without needing to specify the parameter types explicitly. Having a syntax for specifying parameter variance allows for a more sound type system in some areas.
Performance has historically been pretty bad but it's been getting better with every release. The tooling has also seen some improvements after Facebook nuked their own Nuclide-editor project and moved on to endorsing Language Server Protocol and VSCode. It's still far, far behind TypeScript, though. It's also a bit worrying that the community seems to be getting smaller, not bigger, with posts like this popping up.
A few more random thoughts that popped into my head:
Flow's Windows support is pretty buggy. We have some team members who prefer Windows as their dev machine and it looks... painful.
Flow supports the upcoming `?.` operator and overall they seem more open to introducing features that are in a development phase and might be changed/deprecated in the future. I guess it's a matter of perspective if that's good or bad, but man I'm going to be sad if we end up making the change and I have to convert all those nice and clean `foo?.bar?.baz` chains into some unreadable multiline monstrosity or calls to some random getter library.
Flow's exact object types [1] are really useful and have prevented actual bugs when used with optional properties. TypeScript's concept of "freshness" [2] does prevent many if not most of these bugs though.
> Flow supports the upcoming `?.` operator and overall they seem more open to introducing features that are in a development phase
It's funny because Typescript started out by trying to get ahead of a lot of features and "settling down" to trying to stick to (mostly) only TC39 Stage 3+ features has been a maturation that has been good for the language. It's interesting that Flow seems to be moving the other direction.
A benefit to making it easier to pipeline Typescript inside Babel is that it is no longer entirely on Typescript to transpile every possible wishlist language proposal. This is also why a lot of people preferred Flow because you could just slot Flow checking before or after certain transforms. Typescript is getting better about fitting into the middle of a Babel transform stack so that you might have some basic preprocessing steps of Stage 2 or earlier or non-standards track things that Babel supports before type checking.
(The Optional Chaining proposal for `?.` is currently in Stage 2 in TC39. Indications seem to be that at least some of the Typescript devs are ready to champion that feature in the very minute it hits Stage 3.)
Ironically, one of the easier ways TypeScript has been able to work quickly is by skipping the type inference Flow does. It’s a trade off. The Flow team was advised that this approach would have performance problems early on in the project, and opted to focus on type system features first and perf second.
Flow's perf issues has more to do with bugs than anything. Its usually pretty near instant even on big projects, but do one little thing wrong somewhere that hits an edge case and everything goes to hell and you'll never find where it came from.
I recently spent some time introducing typescript to a javascript code base. My background is mostly backend/JVM, so I wasn't the perfect person to be doing this obviously. But as there was nobody else and I needed to own this stuff, I put in the work and got things done.
My impressions are mostly positive in the sense that if you are doing JS, you are better off doing TS. The tooling is great and you can start with simple changes that almost immediately start giving clear benefits in terms of typing related warnings and other improvements. Typescript is minimally intrusive in the sense that all js is valid ts and you can gradually improve it by adding type annotations and addressing warnings.
The benefits are a vastly improved safety net at a very minimal cost, smarter tools, and more confidence that a given piece of code will not break with some entirely preventable error (like an NPE). Excluding whole categories of bugs is a good thing. For new code, I consider it a no brainer. Most new projects seem to default to using typescript so I guess more people have come to the same conclusion. Lots of upsides, no real downsides that I know off. You'd have to be pretty stubborn to opt out at this point. For older code that you still care about, migrating is a good investment as well: you will improve the code. Any other code you probably need to get rid off anyway.
That being said, I believe typescript is merely a gateway drug. Typescript is great and you definitely should use the strict mode. But even in the strict mode it still inherits a lot of cruft from javascript and this makes life needlessly hard. Other languages don't have this problem and the progression from the js/ts ecosystem to other things is very obvious with some younger full stack engineers I've known for a few years. Go and Rust seem particularly popular lately. And even Kotlin seems to be well liked (big fan myself).
Better still, a lot of these languages are coming to the browser (via WASM or transpilation). Inevitably, some projects will start moving away from defaulting to the js/ts ecosystem for frontend work. Right now you'd be a very early adopter but things are improving rapidly and there are some early adopters. I played a bit with kotlin-js recently and while it still has lots of rough edges, it actually works quite well. Not quite a drop-in replacement for typescript just yet but most of that is related to consuming javascript from kotlin; which I'd argue is short term needed but long term undesirable and likely to become less important as native kotlin frameworks emerge (e.g. kvision is a popular one). Tip, parcel recently added kotlin support; works without extra configuration even.
I absolutely agree on TypeScript, I love that it's opinionated and simply versioned. Every time I have to step back into a codebase that uses Babel and X number of different plugins it just tires me. Typescript is just Typescript, and the compiler works fantastically well.
> Better still, a lot of these languages are coming to the browser (via WASM or transpilation)
I think this has always been a concern - CoffeeScript had its moment in the sun, then the good parts got merged into JS and it died out. We'll see if that happens again.
WASM concerns me, though, because of the utility you outline, which doesn't actually serve the end-user in any way shape, or form. Much like we're seeing tiny webapps made with React (and requiring browsers to download and parse the accompanying runtime), I worry we're going to see sites using e.g. Kotlin in WASM simply because that's the developer preference, totally ignoring the fact that it means every user is going to have to download a Kotlin runtime and garbage collector despite there being a perfectly good JavaScript one sitting right there.
> CoffeeScript had its moment in the sun, then the good parts got merged into JS
No. Some good parts have been merged, but so many good parts will never get to ESxx or Typescript. Most dev's just don't see it because they do not know Coffeescript well enough. Compared to Coffeescript, Babel is a total mess with all it's options/plugs config etc..
I rarely have type issues in JS, Python or Coffescript. I do have type issues in Typescript though. I fix that with 'any', just like my collegues. A Typescript codebase is about 2x bigger than a Coffeescript codebase, that means about 2 times more chance for bugs. But it's totally amazing to see the Typescript proponents considering the language to be a godsend. I moved from C/C++ to Javascript to get rid of static typing, it's part of my love for JS, but now that joy is being destroyed by static type enthusiasts.
I've never worked in a pretty Typescript codebase ever, same with JS, so many bad constructs, bad naming, bad architecture, etc, etc.. IMAO the real problems with codebases cannot be fixed with Typescript, the problems are way too big and complex for that.
I still don't understand why JS dev's that crave for types don't do Elm or Purescript instead of Typescript.
> A Typescript codebase is about 2x bigger than a Coffeescript codebase, that means about 2 times more chance for bugs
That's pretty obviously not true. If that extra code is adding type annotations then it is removing potential bugs because the code won't compile if you accidentally combine incompatible types.
All these issues are being worked on. If you use kotlin-js, the output is javascript. There are no GC issues, you use the javascript GC.
WASM is currently indeed bottlenecked on the lack of GC. This is being worked on. I expect a lot of progress over the next two years that will gradually remove most of the obstacles on this and other fronts. None of these are fundamental issues; it's just a matter of things not being perfect yet. Despite this, there's already a lot of early adoption. But I agree that Kotlin on WASM is short term not ideal. In the same way C#/Blazor currently only makes sense for enterprise application where download size is less of a concern. Rust does not really need GC, which is why is a popular language for this already.
As far as bloat is concerned; react is actually pretty horrible. I've seen simple react applications go over 1 MB for no good reason. I'd say the react ecosystem is actually a main driver for people wanting to have some alternatives. I actually prefer more lean approaches. Vue.js seems nice and I'm aware of a few nice lightweight dom abstractions (e.g. redom is nice).
The problem with JavaScript is that it's a terrible example of a dynamically typed language.
> The benefits are a vastly improved safety net at a very minimal cost, smarter tools
That's entirely subjective. As far as tooling, I get the impression from my coworkers and what I've seen online that people that love TypeScript love big bloated IDEs. They love having their IDE tell them what to do. And, personally, I find their usage of IDEs to be a crutch that prevents them from transitioning from a plateau of mediocrity to being a great developer. They don't understand the code. They merely throw things together that match the types that their IDE tells them, like LEGOs. Then they spend hours trying to figure out why their code doesn't work.
As far as the cost goes, my experience has been vastly different than yours. TypeScript is killing my organization. For no real benefit. The warnings it produces are for theoretic bugs rather than actual bugs. The warnings TypeScript produces are incomprehensible to most developers. And when they finally quiet TypeScript down, they don't realize they have the wrong types! No one seems to fully realize how easy it is to get TypeScript wrong. Your code gets littered with ts-ignore and "any" types and suddenly you have a mountain of tech debt. Code becomes ugly and difficult to read with type annotations. It's hard to imagine anyone actually winning in this war of attrition between clean, simple, readable code and TypeScript.
If you're doing TypeScript and having an easy time of it, you're most likely doing it wrong. TypeScript is incredibly nuanced.
Management loves TypeScript though. It's a shield for responsibility. When bugs happen (and they still will with TypeScript), they can point to TypeScript as something they tried. They didn't fail the organization, their technology failed them.
The safety net thing is absolutely factual. Typescript will tell you "this is a type error, fix it". Javascript will not and fail at run time for the same error. So, one is an error that is stopped from happening, the other is a preventable error that your safety net (or lack of one) fails to catch. That was only excusable as long as there was no feasible safety net. Now that there is one, deliberately opting out from it is simply unprofessional and irresponsible.
This in a nutshell is why world + dog is introducing typescript to their codebases. It obviously won't catch all bugs but it will catch more of them. The alternative of not using it simply inexcusable and the people arguing against it tend to not have a very solid case and indeed focus on what arguably is highly subjective like e.g. your claim that it is "ugly". IMHO JS without types is ugly (opinion) and less safe (fact).
I agree typescript is not perfect; I agree it is super sloppy, actually. Even the strict mode still allows a lot of stuff that you should probably should not do (like slapping the any type all over the place). That's why I call it a gateway drug. If you like the little that typescript does, there are other languages that are better.
If typescript is "killing your organization", you should consider leaving. There are all sorts of reasons for organizations to become dysfunctional. The problem always boils down to people, not technology. I'd argue that given your statements, you are possibly part of the problem and not the solution here.
Personally I'm a fan of sprinkling the code with manual checks, conversions, and early friendly error messages. Rather then spraying it with type annotations. Practically when developing and there's an error, you add a check as early as possible, that would catch the bug, like at the beginning of the function, throwing a human readable error with some added debug data. For example, lets say you have done some detective work to locate the source of a bug, and figured out the earliest possible place to detect it is in the foo() function, where the bug was caused by a missing "baz" property ...
function foo(bar) {
if(foo.baz == undefined) throw new Error("foo need to have a baz! foo.baz=" + foo.baz + " foo=" + JSON.stringify(foo, null, 2));
}
Typescript could probably detect the bug in the first place if you had used interface or what not, and typed everything. Static typing (and also tests) can however slow you down when you are quickly iterating and re-writing. And at the end of the day when you have a working prototype/MVP, and have added a few manual checks that will throw early if there's a bug. Are you gonna spend your time type annotating it all (or writing tests) or are you going to spend that time marketing ?
If the product receive continuous work there will however be a tipping point where tests are needed. And the checks you added will be of help as errors will come early. But when you have tests in place, is it really worth it to spray-paint the code with annotation and make it static ?
There are already tools today that can infer most types, and give you auto-completion already.
I don't want to have to remember the properties of every object, or the arguments to every function, or a myriad of other things. I could be using that time and energy to work on other things that are more important.
I've been using TypeScript for over 1 year after 10 years of JavaScript and I really don't like it.
It slows me and the team down and creates more problems than it solves. It doesn't even ensure type safety because there is no runtime type validation for JSON objects received from the API.
It's disturbing that so few people can see how useless it is. To me, it's extremely obvious.
TypeScript is a hack of epic proportions and every so often the reality rears its ugly head in the form of failed source mapping, version compatibility issues, unexpected types during runtime, poor architectural decisions aimed at pleasing the compiler instead of fulfilling project goals.
TypeScript is a very poor way to model real-world systems because it incorrectly assumes that real-world entities have a fixed type schema. This is obviously wrong.
Real-world objects change over time and most things cannot be categorised clearly.
A tadpole turns into a frog and learns the ability to walk. Some people are disabled and cannot walk. Some cars have 6 wheels.
There is no fixed type schema for anything in the real world; it is filled with anomalies so why should we design systems to model such unrealistic objects?
Why not force the developers to account for as many cases and schemas as possible, it's our job!
As someone who has also been using TypeScript for over a year and JavaScript for over 10 years, I've had a different take on it.
Disclaimer, I freaking love JavaScript and TypeScript.
> TypeScript is a hack of epic proportions and every so often the reality rears its ugly head in the form of failed source mapping, version compatibility issues, unexpected types during runtime, poor architectural decisions aimed at pleasing the compiler instead of fulfilling project goals.
This just sounds like JavaScript to me. Maybe TypeScript has helped elucidate those problems for you. TypeScript adds some syntax to help formalize solutions to these problems that existed in JavaScript already.
> TypeScript is a very poor way to model real-world systems because it incorrectly assumes that real-world entities have a fixed type schema
This sounds to me like programmer error. TypeScript provides facilities for unknown structures, or structures that have multiple possible definitions. There is the union type for which we can tell the compiler that a value can be of type `A[ or B[ or C[ or... ]]]`. Perhaps even more powerful is optional types. In interface you'd describe an optional field with a question mark, like `{ foo?: string }`. Alternatively, you could use the union type described above, unioned with `undefined`. These structures help you create types that actually do model the uncertainty of data coming into your program. If you're not using these techniques, then you're missing out! Having the type-checker yell at me because I didn't check for the undefined case is a really really cool thing.
> Why not force the developers to account for as many cases and schemas as possible, it's our job!
I would argue that TypeScript provides the vocabulary for doing just that and that JavaScript provides no such niceties. Sure, you provide run-time checks on a piece of datas validity, but there's no system to check that run-time behavior before code gets merged to master.
If you're not running `"strict": true` and annotating your TS code with optional values when data is coming into your program, you might have a bad time.
> It doesn't even ensure type safety because there is no runtime type validation for JSON objects received from the API.
It ensures internal type safety. If you don't have a lot of complexity on your side (e.g., a large client-side app) that may not be worthwhile to you, but it's certainly a thing it does, and a reason static typing is generally considered to be of value, particularly in large, multiprogrammer and, even moreso, multi-team projects.
Typescript has been useful for my team to reduce the number of unit tests and constant checking of number and order of parameters in functions, presence of null/undefined values, etc - when compared to JS obviously. It works particularly well when coupled with JSON schema validation that checks that the contract of your api is not ignored (TS types map very easily to and from JSON schemas).
As for your third paragraph, we follow some degree of functional programming precepts, which implies we avoid mutability as much as possible, so I can't comment much on the problems of mutating TS. I enthusiastically recommend pursuing immutability though, TS or not.
I still don't get it. You can also add rigorous schema validation to your API endpoints with plain JS so you don't need to worry about strange inputs in your tests either. This is not unique to TS.
Perhaps I didn't explain it well - my point was not that you need TS to validate your inputs, it was that you can use TS to guarantee internal consistency of data, and that said internal consistency will work well as long as you ensure that the input is safe -which you do need to implement through other means like json schema. What TS offers in this context is to ensure that you didn't call a function with arguments present in the wrong order, that you didn't assign one variable instead of another, that you didn't forget to include one last argument...
All things that come up relatively frequently in vanilla JS during refactoring processes.
Yes, but it feels like more work and it's more verbose. In Typescript, it's also more or less convention, where as in JS it's just nice to have. I have less guesswork to do with Typescript. There's value in that.
It doesn't sound to me that you really took it seriously and try.
For example for what you said about "JSON objects received from the API" has no type validation, it doesn't unless you define the types for it. You can write interfaces that defines the API responses and use it across whole code base.
This is incorrect, merely declaring a type for all your API inputs does not prevent clients (especially a bad actor) from actually sending objects of different types at runtime;
and when they do, your code will crash catastrophically. Try it.
You need to do manual schema validation, just like you did with JavaScript. TypeScript adds 0 value. The type only gives you an illusion of safety, which is worse than no safety at all.
"Types are useless because they don't version my wire protocol!" is a wholly specious argument. There are a variety of RPC protocols that address versioning in a variety of ways; this isn't a typechecker responsibility. You can generate Typescript interfaces from protobufs if you fancy.
Typescript makes sure that all your code agrees with the protocol definition you have chosen. That alone is immensely valuable.
Protobufs is an antipattern. You can't expect external systems to agree with your system's type definitions. It just gives other people extra work when integrating with your system.
JSON is better because it allows external services to interpret the data in their own way regardless of their type system.
Typescript only checks all that at compile time though. The complaint here is that even a typed json.Parse<T> :(json:string)=>T will not actually give any type checking when you pass it a string containing any schema.
When you type json.Parse<T> you are taking on the responsibility that what is returned is actually T. If you want the type checker's help that you are sure it might not be T, then ask for it: json.Parse<unknown> or json.Parse<Partial<T>> is maybe more accurate, and would force you to write runtime checks. Partial<T> in particular is a great helper type that is often exactly what a lot of want in a json.Parse situation: gives you the field autocomplete you want, but reminds you that they may be undefined.
A sound type system shouldn't allow you to write such a parser function without either you lying in its implementation or having to use unsafe code.
In a year or two, once everyone has switched to use `unknown` instead of `any` in functions which at runtime return… uhm… unknown values, we'll all be much safer. Because we'll be forced to use proper parsers and validators.
Used properly, runtime checking and types go well together. Types help you remember whether incoming data has been validated. You can use them to make sure your code does runtime checking exactly once, rather than multiple times or not at all due to changes in different packages that can introduce security bugs. This can make security reviews a lot easier.
I don't know if that's commonly done in Typescript, though? It seems more common in web apps to trust your own server to send you good data.
There are libraries that let you define runtime schema objects which you can invoke to validate incoming data — and they also let you extract static types from those schemas so you don’t have to write things twice:
"Typescript adds 0 value" to taking user input. Sure. I mean, it's kinda missing a lot of nuance--especially given that it's kind of smart to let-it-crash universe where bad-actor input making your handler crash or fail, so long as it does so safely, is not the end of the world--but yes, you do in fact have to validate user input. You have to do that in every other language too, of course, and stuff like `class-transformer` or `runtypes` or `io-ts` exists to make it easier and safer, but yes. You do.
What about module boundaries between code? What about even the basics of knowing what you're passing into a function is correct?
Me, I write a lot of TypeScript. I've never written code on top of the Node virtual machine as quickly or good or as correct because I'm not stuck resorting to nonsense tests around "well, what do you do if you pass the wrong type to this function?" and I'm not validating internal arguments because TypeScript told you if it was wrong, this is not my bug. Instead I validate user input at the edges (less because of "bad actors" and more to provide helpful messages to the consumers of my libraries and APIs) and then I have a snappy and reasonably correct compiler yelling at me when I do something wrong, immediately after doing so. And because my entire ecosystem, past that scary user-input edge, is also in TypeScript, I am much more sure of the code I'm writing. And, as mentioned, I write it way, way faster.
So if you care about correctness and code quality, why is "well, input validation on the edge is harder" so much more important to you than that sort of thing?
There's a weird impulse out there to try to pretend the limitations of static type-checking don't exist... namely, that it's static. That is, it's build-time checking and doesn't provide guarantees at runtime.
For tightly coupled systems were you control all tiers, you can stretch the value of static type checking by ensuring that changes to back, middle, and front all roll out together. But obviously, you're seriously limiting the scalability of your product and agility of your releases when you do this (you need a command-and-control dev communication structure). If you want static type checking to make guarantees beyond the local component, this is a major architectural commitment. To maximize the "guarantees" of static type-checking, it will affect the topology of your entire product, including the release process. (How much downtime is acceptable per release? What limitations are you willing to accept in terms of distributed scalability? Is it acceptable that user sessions become invalid for a release and how will you handle the UX for this?)
Not saying you shouldn't do it, but go in with your eyes open.
Now, static tools certainly have their uses. But:
(1) type-checking is just one limited case. e.g., go get eslint, turn on almost all the rules, and work with that for a few weeks. After you get over the initial shock, you'll get a lot of benefit from from it and it will last for years.
(2) it doesn't help you with anything external to the source file that's not tightly coupled through some additional control mechanism. (Note that you pay a price for control mechanisms.) So, it's a small solution to small problems. Quite nice. But limited without paying an additional price, which may be quite expensive depending on the other goals of your system.
I don't agree with the previous poster's assertion that it's a useless hack. But I understand why one might feel that way. The utility of static type-checking seems to be greatly oversold.
If you look at a typical call stack - in any language - going from the frontend all the way to the database, there will be hundreds of lines. Two, maybe three of those will be remote jumps.
Every one of those calls is an opportunity for a contract mismatch. Even if static type checking misses a half-percent of your calls, it's still working to enforce 99.5% of your contracts. The value of that is hard to oversell.
There’s nothing wrong with static type checking, It’s generally good and will prevent certain kinds of problems with out the cost of unit tests. I’m all for static checking generally, not just for types.
My point was to point out the limitations. I might be wrong, but people seem to often ignore these limitations. This oversells the benefits and leads to frustrated people like the one whose comment I originally responded to.
99.5% is not nothing, but not something you can live with in most systems so you are probably going to need something more. Also, you are going to be a lot happier if the vast majority of those hundreds of lines in your cross-system call stack are completely agnostic to the types of your problem domain and you want to be careful to minimize the coupling between components living on different sides of the jumps you mention. You can do that, of course, with static types, but it takes more care and intentional design and you really need to acknowledge the issues before you can get it reasonably right.
Tangentially, but in a similar vein, this is my gripe with TypeScript: It leads you down a path that takes the best part of JavaScript out: namely it's dynamicity, and prototypical object orientation, and leaves you with a hamstrung, not-so-good statically typed OO language. When you view it in that light, it hardly stands up to other options out there, IMO of course.
That's why I was lead to something like ReasonML because it solved the things I was already trying to do with my front end code (keep my renders in React immutable, minimize side effects, use more functional paradigms, have strong typing), but it did it with a much more powerful functional language, with a world class type system in which only Haskell rivals.
TypeScript does not take out dynamic typing or prototypical object orientation. If you can more cleanly communicate something dynamically, do it. `any` is right there. (And I do this sometimes! Of course, code where I do this is invariably where most of my unit testing has to be, but I have the choice, and I'm making it.)
And, not to be pointy, but I always get a real weird vibe about people who come rolling in professing that TypeScript isn't a backhoe when it professes to be a shovel. TypeScript is not about a "world class type system" and that's never been the claim. It's about reducing the ways in which normal developers can shoot themselves in the foot. If ReasonML or whatever equally bloggable thing scratches your itch, sure, but you're never getting the 95th percentile of developers, to say nothing of the 50th, to write it--and that 50th, and that 95th, percentile can benefit from better tools, too.
I see what you’re trying to say here, but Reason (and its compiler BuckleScript) aren’t the kind of puristic language folks might think they are. BuckleScript has one of the most comprehensive way to bind to whatever piece of JS you have: https://bucklescript.github.io/ and you can also just include raw JS code as a last resort, all type checked still, while keeping type soundness (in this case, almost like `any` but not really).
HN’s too short to describe the extent of it; if you’d like some language that shares a similar spirit as TypeScript (with extra features such as native compilation), please do check out Reason and BuckleScript. Our main goals are great JS interop, fast build, robust type system and a familiar syntax for JS users.
I'm familiar with the project, and with both F# and OCaml generally. ;) And I'm not saying it's bad or anything, to be clear. I am saying that it's functionally impenetrable to most programmers. To draw an analogy to something less contentious in this crowd, Elixir, even though I quite like it, is doomed to the same fate.
TypeScript is the good-enoughest thing that doesn't surprise, and I think that's enough to put it over the top. (It helps that, in this case, "good enough" is also "very good".)
Thank you for all the work you do btw -- Reason is awesome and I am having a lot of fun learning. Interop seemed daunting at first but after I tried it, it was a great experience.
> TypeScript does not take out dynamic typing or prototypical object orientation
Hence my wording, leads you down the path not forces you to. More often than not, your TypeScript codebase (at least the part you have written yourself or by your team) will be Classical OO style and fully typed, and your code reviews will consist of copious amounts of "we should add a type here"
Well, as a someone who had the same initial feelings with TypeScript when it came "oh it slows me down, these types are just a waste of time", I get where you're coming from but think that you have some other issues than TypeScript with your team and project. First of I'd want to say that most of your complaints about "no runtime type validation" in the context of TypeScript/JavaScript is just how unreliable JS is as a language. You just can never be sure and even with TS you can get all the boilerplate of TS with zero benefits if you resort to using `any` anytime you feel too lazy to add difficult types.
Which leads to my next point, the slowness of writing TS compared to JS. I don't know how you maintain your code bases but from my experience any time a new programmer comes in who doesn't know anything about the code, they'll be from time to time wondering, in their heads or aloud, what the hell is the type of some parameter or why something fails on some weird undefined value bug. There's just so much trial and error with JS whereas with TS I don't have to look up at the broken website to see all the errors hitting me as it's all caught with the compiler. Pretty basic static typing stuff but it all comes down to more time writing code, sure, but less time over time maintaining it. And overall the readability of your code is just night and a day with special emphasis towards young programmers or those unfamiliar with the codebase.
What I think has happened is, that you've been bitten by some weird problem regarding API endpoints and incorrectly assumed your typings would hold with an external data source. That just sounds poor programming (don't you use validation libraries such as Joi?) and frankly, a problem entirely of JS itself that such anomalies would happen. If my API starts returning arrays instead of objects sure as hell I'd be angry too but that's just not how it should work. Not a problem of TypeScript, at all.
About the other problems - haven't faced those myself. Most problems I've had have been with writing the correct types especially when they get complex with generics. Sometimes TSC has been annoying and also exporting types into a single typing file seemed quite laborious last time I tried.
Also I think part of the problem is that TypeScript encourages developers to write interfaces that have complex parameters (e.g. accept instances of specific classes) whereas JavaScript encourages simpler parameters like strings, numbers or plain JSON.
In OOP, passing complex instances to functions across different files is dangerous because the different files may all end up affecting the same instance's internal state and you don't know which file is responsible for what change (and it can lead to conflicting states/bugs). The absence of types in JavaScript tends to discourage this design precisely because it makes it difficult to express those kinds of complex/rigid class relationships.
JS encourages developers to keep each kind of "live" instance in a single file and only return raw data from that file. For me, restricting all instance mutations to one file is the secret to writing good OOP code which has clear separation of concerns.
TypeScript doesn't encourage returning classes. Writing classes just as data handlers is still verbose and clumsy. You can have a team that's writing Java in TypeScript and see that, but that's not conventional at all.
Instead, TypeScript encourages returning structurally typed interfaces. Which are just data.
>It doesn't even ensure type safety because there is no runtime type validation for JSON objects received from the API.
what do you mean by type validation of JSON objects? If you mean, that you can for exammple pass string to object property defined as number, then I must warn you, that this is problem in all languages (eg. most java json serializers, will throw exception). Anyway if you need to ensure all object properties have correct type, you should write validator/transformer (there are already libraries that can do it for you).
On this planet it is possible to do runtime type validation with typescript. It's not an out of the box feature but a number of libraries allow it, e.g. https://github.com/gcanti/io-ts
Yeah, you're right, and since vanilla javascript does have runtime type validation that's a reason not to use Typescript. /s
The documentation, tests, breaking run attempts when developing, and flipping back and forth between different source files it replaces, almost painlessly, are 100% worth it.
And you can trivially "nope" out of it anywhere you want if it's getting in your way too much, and congrats, you're back to lovely, pain-free, JS. /s
ActionScript 3 was one of my first programming languages and I used it for many years; it's also based on the ECMAScript standard and very similar to TypeScript so I should be biased to like TypeScript, but I really don't.
I was a big fan of static types for many years (I also did Java and C++) so I understand your point of view perfectly but I also understand that it is incorrect.
The fact that my POV seems so bizzare suggests that you haven't considered it thoroughly before.
Your view is correct but irresponsible in practice. Validating the schema of all parameters of every method call (because you can never trust the caller in dynamic languages) would be prohibitively expensive. A compiler which injected these checks into every method preamble would have incredible correctness, but would create functionally useless code (if performance is a feature).
> C++
What happens when you call main(argc, argv) with the incorrect argc parameter? There the exact same problem that TypeScript faces at the start of every single C/++ program.
Validate your payloads with JSON schema if you don't trust the server/backend. JSON schema is designed to type-check at runtime and is therefore the correct tool for the job.
Don't curse the hammer if it is unable to loosen bolts.
Stop being dismissive. You know that’s not the point.
I have a few applications that use the same API, and it’s consumed using a shared library in Typescript. There’s a little bit of code that takes raw messages, parses and validates them, then returns typed objects. This means we can use those typed objects confidently throughout the rest of the codebase, making some useful guarantees about the content of them and detecting errors at compile time. Parsing code is isolated and tested in a small area, like you light use unsafe code in Rust.
Yes, it would be nice if this was easier, such that parsing was automatically handled based on those types (and I don’t doubt that there are projects out there that do this). But that obviously doesn’t mean that no value is added.
It’s totally fine if Typescript doesn’t suit your applications, or your way of working - but it might be useful to consider that other people aren’t idiots.
> Stop being dismissive. You know that’s not the point.
Please edit such swipes out of your comments here, regardless of how wrong or annoying you find someone else's comment. Your post would be much better without those bits and the swipe at the end.
All those examples in your last paragraph should be accounted for in data modeling and in the domain logic. Saying a type checking tool somehow prevents this from happening is asinine.
Check out Rich Hickey on this, eg in the 10 years of Clojure talk (or the transcript).
Static typing requires you to express things in a way that is easily formally provable by the type system. It's much easier to write correct code (=working parer) than the correctness proof in a limited type system. This goes for higher order operations on schemas / types too, eg schemas are easier to make composeable than types.
My only real pain point with TypeScript so far is with a codebase where some parts run in Node, some parts run in the browser, and some parts are libraries that can be imported by either. The best solution I have found is to separate out all TS code into three directories and have multiple tsconfig, but it often doesn't work nicely with the rest of the ecosystem.
I've been reading the comments, and sorry, I can't help it. Every time I read about (or try) some trendy thing in JS/Front-end world, I feel irritated and sad. Every single "big promise" hype that supposed to help us to "fix" issues with front-end development adds more complexity and frustration. jQuery was awesome, then "okay", then become bad, then later really, really bad. Same with Backbone. Same with Angular and Web components. Then Coffescript, Livescript, GorillaScript, IcedCoffeeScript and other "x-cripts", Dart, Typescript and Flow, Traceur and Babel. Then Grunt, Gulp, Browserify, WebPack. React came, and we got Flux, Redux, Redux Saga, MobX. And then some stuff that's actually not too bad for whatever reasons gets mostly ignored, e.g. Meteor, Ember, Cycle.js.
I think fundamentally something deeply wrong with all that, and I don't know when and how we're going actually to fix it. After years of trying, I finally decided enough is enough and started looking for deferentially alternative solutions. I like Purescript and Elm. ReasonML looks promising. Currently, I'm using Clojurescript. It is not a silver bullet, but it is an astonishingly practical choice. I know - it won't work for everyone. The majority would dismiss it almost immediately (mostly for the wrong reasons).
I'm not advocating for it. I'm just saying that I feel Typescript is not that magical pill that makes everything better. If you follow @garybernhardt on Twitter, I think he may feel the same.
So you have seen the JS world progressing and feel sad about that they haven't just found out the "best way" from the beginning? I think it's marvelous how fast JS moves and outdated solutions are phased out in new projects for better and more reliable methods (although I admit some JS developers pick the wrong tools for wrong reasons). The act of having to constantly learn and think about a better way of doing things has at least kept me sharp and meticulously pondering the pros and cons of each way of doing things.
Sure the churn can be discouraging and if you wanted to be able to write the same way with same tools to retirement I wonder why you'd even consider doing frontend development. I am quite happy with the tools I use (Typescript, React, Mobx, StyledComponents) and have felt no need to change for something else. They are tools I am very productive with, and all the developers I've worked with have been able to get quickly on track with using them. No huge mental leap from JS to TS as with functional languages like Clojure or Elm.
Although it's my opinion that, JS itself should be eventually be phased out for something better for majority of frontend software. It just has too much old baggage so it would be best to just switch for something new completely. However, there's something mysteriously intriguing about JS in that allows so great flexibility, in good and bad, that not just any language can replace it. But nowadays I don't do much complaining about JS stuff anymore. I have found my sweet spot and I'm mostly just busy getting stuff done and there's things far worse in programming than how "bad JS is".
Somewhat hijacking this thread to say that if you have ideas for improvements to TypeScript's documentation or website, we're currently asking for ideas:
I've been a big fan of TypeScript for a few years, but lately I've been Considering Removing Typescript[1] from my project, because it seems to have just as many drawbacks as benefits, and the freshness of pure JavaScript is starting to look appealing again.
But I haven't heard anyone take this perspective and I'd be really curious to hear people's thoughts on this idea.
> Browsers don’t support it, so it adds an extra build step. Type-completion has gotten slower the bigger our project gets.
This is something a faster computer can mitigate. Also I would compare the time you spend guessing what parameters actually are in vanilla JS vs the seconds you lose waiting for a Typescript build. Silicon time is much cheaper than carbon time.
I don't understand how anyone doesn't use Typescript for large projects. Javascript is just too lenient. It doesn't even complain when you're not matching a method signature that you call; you can't even catch these ninja errors let alone explicitly know they dropped on you. There's a lot of value when you have a system that adds more checks and validation before runtime.
If we were talking about Ruby or Python, you'd have a stronger point. However Typescript's benefits far outweigh its drawbacks in light of Javascript's excessive lenience. Since, you're complaining about types, have you made good use of Typescript's interfaces and class inheritance? They're pretty powerful and even more flexible than something like Java.
Yes, I use TypeScript types very idiomatically and have received very positive feedback on my TypeScript code. But just running `tsc` takes >10 seconds on a relatively small project, and IDE support is becoming proportionately slow, e.g. hovering over anything to show its type often takes 5-10 seconds before VS Code will actually have something to show me. It's becoming painful, and that feature which I once heavily relied on has become nearly useless to me.
Is the time spent compiling drastically more than the time spent debugging code because of the lack of types? A few seconds of compiling is because than the potential hours wasted on debugging. Besides, you can mitigate compile times by getting a faster computer. Silicon time is still way cheaper than carbon time.
It's not just "seconds wasted in compiling". It's having my IDE say "thinking..." for 3-10 seconds every time I want to see what type something is. Or having create-react-app load for 5-15 seconds, every time I save any file before letting my browser update (even if I press refresh manually), because it's compiling. And when this happens on a minute-by-minute basis, it drastically slows down development time.
I converted my work codebase to TS and it provided a lot more benefits than just types, including a greater understanding of the architecture. I was able to convert about ~400 files in around a week without any sort of plugins
1. Both TypeScript and Flow are gradual type systems. They both explicitly allow escaping the safety and coverage of a type system, either to allow easier interop with existing JavaScript packages or to allow existing JavaScript codebases to incrementally adopt types. So there are always holes in which errors are reported, regardless of whether they had to silence some explicitly or not. By measuring the extent, they have a yardstick against which they can drive further type adoption.
2. Repeatability is the only way to do large code migrations. Write the steps in a script, run it, find the bugs in the script, fix the script, and repeat. With 300k lines of code no doubt they have dozens or hundreds of people committing daily. So a script is the only way to sneak in during off-hours, get something in that doesn’t race to conflict with someone else, and land the change. A long running, manually crafter branch here would not work—it would effectively operate as a fork of the codebase for the entire duration of the migration, with all the downsides that a fork entails.
Kudos to the team; this is a very impressive feat!