This is in response to
Article No Longer Available
I have mixed feelings about Typescript. To quote Indian Jones, "you call that a type, this is a type."
If this is where people get their opinions of static typing, no wonder they think they should live without types.
On the other hand, trying to quantify root cause of an issue is not trivial. Generally people are not tracking these types of things and it is only a "sense" of the situation. I want to summarize some of my experiences with types and talk to an anecdote.
Experience
Types
- Help with code reuse
- Help with refactoring
- Help guide programmers to correct usage
This first point is really about the next two; you do not need types to make reusable code. If the programmer misuses your library then it isn't very reusable. If you can't make refactoring changes because others might be using it, isn't very reusable.
Let's consider the simplist refactor, rename. Your IDE will very well replace all the usage in the code and you'll be confident things are still working the same. But you have a development team, they are working off branches, some work has practically died on other branches. How do we make sure that the rename propagates to the code in these branches?
With a static typed language, this is a build error. An annoying build error, but preventative nevertheless.
Build failure can occur from attempts to pass objects without the needed properties. And when using a language with compile time execution, these static guarantees can be quite helpful. (plug for D here)
Anecdote
Recently a bug was introduced into some backend code when types were introduced (yes static typed language can work with untyped data). What happened was the type defined expected data before passing the information on to the next system. The previous implementation would blindly pass the blob on.
The previous implementation worked perfectly, the introduction of types was caught by my extensive introduction tests. Why am I so happy that we introduced this bug?
The two systems had a mismatch in expectations for what was contained in the "fault tolerant" web. The system using the data had built its expectations off the existing production data. The type was built from the new data source which did not contain such fields. Our conversations lead to communicating our concerns to the upstream 3rd party.
As my tests use a mock of the 3rd party, had the type been built from our expectations, we would not have caught this issue as we would not have had influence from the real 3rd party API. That is on me, but it all panned out that a bug introduced by types identified issues upstream.
Top comments (37)
My issue is the simplistic notion among TypeScript devotees that TypeScript makes their code "safe" - often because they haven't been exposed to anything better than Java or C#.
any
are punching holes into their safety net.any
have been eliminated they are still dealing with the equivalent of a leaky barrel in terms of type safety (A Note on Soundness: "TypeScript’s type system allows certain operations that can’t be known at compile-time to be safe.").Anybody who thinks that TypeScript "has their back" is living an illusion (and needs to try Rust for comparison).
Anybody who is actually serious about "type safety" would dismiss TypeScript out of hand and move to something like ReScript:
But there is a cost - JavaScript interop with something like ReScript is a lot more work.
But if interop is the primary concern, why did TypeScript introduce its own non-compatible syntax extensions? The limited value that TypeScript can actually offer can be easily derived from type annotations in comments (a la JSDoc TS - Reference) for occasional static analysis, leaving the (no-build-step-required) ECMAScript syntax intact.
The trouble with TypeScript:
FYI: there is actually an article on dev.to: The Trouble with TypeScript
My opinion too.
I read a paper [0] that analyzed how much bugs TypeScript really found and it stated something about ~15%, which isn't that much when take the struggles people have while actually using TS in their daily work.
Would be interesting to do these tests with ReScript.
[0] earlbarr.com/publications/typestud...
Sorry QA perspective, I don't really care that it is a struggle, I'll take 15%. Again how I feel about Typescript suggests I don't like it either, that doesn't mean I want javascript instead.
I think web developers brought this on themselves. Type systems are showing to be valuable in reducing bugs and the hatred for working with types means the winning system is the one that straddles both worlds so it can claim you can gradually add types.
I'm not saying static typing is bad. ReScript seems like a step in the right direction.
That "struggle" does diminish "return on investment". And opportunity cost means that effort invested in the struggle may be more effectively spent elsewhere.
Then there is the risk of adopting a technology that could be "end-of-lifed" at the drop of a hat. Microsoft and Google regularly stop supporting their technologies. Flutter is the only thing keeping Dart on life-support - so while the TypeScript user-community seems large enough it isn't clear that there is enough will, enthusiasm and critical mass to continue the product if Microsoft bows out.
Then there is the issue that while there may be a sufficient body of developers singing the praises of TypeScript in terms of "Developer Experience" only very few people seem to be evaluating the quality, volume, and performance of the code generated by the compiler even though they directly affect the non functional aspects of the end product - which can enhance or diminish the end product's core functionality.
Using type-aware tools doesn't replace learning about type-driven development (classes are no substitute for types) which typically requires working with something like OCaml or Haskell. Once you've been through that ringer even your JavaScript should be better (though type annotations and static analysis should be helpful).
Very good points.
Thank you!
My first post seemed to disregard tho concept of deminishing returns. I do think their is a limit but that isn't what I'm seeing with Typescript. My initial return is surely beyond reasonable, but that doesn't the end result isn't.
Many would probably consider the return on functional languages to have a demished return, especially now that so much has been brought into our more frequented languages. I straddle that fence as I have yet to dive deep into one.
But if we had a company push to switch to Haskell with a few developer buy-in, I would not be complaining about the struggle it puts me in to learn those languages.
Again that is another one of those fallacies - functional programming isn't about the presence of features but sometimes the absence of features and more to the point an adjustment in thinking.
While OO is supposed to be about class, responsibility and collaborator functional programming is about solving problems through data transformation.
From that perspective learning a new paradigm in a multi-paradigm setting is problematic because of the familiarity trap - you will always tend towards familiar patterns of thinking unless the constraints of a single paradigm force you to change your thinking. This is most dramatically reflected in the Scala community - teams using Scala as "a better Java" who have no hope of understanding/maintaining code written by the "I'd rather be working in Haskell" teams.
I found the easiest way to learn functional programming was through something like the Erlang MOOC (simply because you didn't have to to worry about types, type classes, laziness etc.) and later the OCaml MOOC for statically typed functional programming. (In the absence of those Learn Functional Programming with Elixir might be worth it).
That's not how it works from what I've seen. Typically the leadership of an organization is already convinced that a particular language is their "secret weapon" and then they hire people already competent with it or who show enough promise to be successfully trained in it.
Unfortunately this familiarity trap is the driving force around user interface design. Due to familiarity we got C, C++, Java, C#, ORM, Typescript.
And while typically the language is chosen early on and the company forms around it, the context here is with Typescript and this very much can come through as a push onto the majority of existing devs (hiring to Typescript for replacement of those who choose to leave). My example of Haskell was not realistic but used as it is my parallel to what javascript devs would be going through.
Rust
unsafe
is analog to TypeScriptany
🤷♀️ doc.rust-lang.org/book/ch19-01-uns...(But I do not know or claim that those 2 type systems are equivalent)
I haven't used Typescript enough, I can't even articulate what is wrong with Typescript, and it has nothing to due with it not being sound.
At one point I was converting a type to any then to the type I wanted and I haven't dived in to what was wrong with my understanding.
In situations where a function is dealing with a parametric type (generics) but it isn't interacting with the actual type value it's sometimes useful to use unknown as a type value.
Furthermore you typically narrow types with type assertions, user-defined type guards or assertion functions.
To me, it's mostly that TS makes JS look like C# and I was quite happy with how JS looked like before.
TS codebases are too OOP heavy for my taste.
I could see how the introduction of typing would feel more like C# OOP than Javascript OOP. But make no mistake, Javascript is very much OOP and Typescript is not like C#.
Could be.
But I really think JS OOP is the right way and chipping away from it goes in the wrong direction.
From the horses mouth: TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web.
Java and C# implement (traditional) "class-based object-orientation" - class membership is static for the lifetime of the class instance (object). In JavaScript classes are a mere "template for creating objects", i.e. during its lifetime an individual object's spec can be changed to align with completely different classes without changing its identity.
JavaScript's "prototype-based dynamic object-oriented programming" is based on Self. I tend to think of JavaScript as Function-Oriented (Yes, JavaScript is a Lisp).
The multiple 800 pound gorillas that I have yet to see TypeScript supporters adequately address are:
@param
/@typedef
/@type
/@return
/etc. annotation, combined with ESLint (or VS Code set to check JS) works just fine. You don't even need to install JSDoc (though you get auto-generated code docs if you do) - it's just a docgen for a function/class comment syntax/doc paradigm that has been standardized across multiple languages.I am not familiar with the TS alternatives, usually the issue I read about equally apply to introducing static typing in any form, see your 2, 3, and 5.
I'm going to first need to restate what I think you are getting at.
It is important to do runtime testing for other important system failures, type issues will be identified during this effort.
Is that accurate?
Thus static type checks are redundant.
Is that also where you are headed?
.3. This is true in all static type languages, the types are a compile time construct. The TS to JS adds the complexity that while working in Typescript you may actually be working with untyped code and you've only declared expectation on that untyped code.
This is much like passing void* in C. The type system is subverted and the compiler will assume you know the type. It is not like C in that JS actually has runtime type information and you will only have corrupt data due to JS loose typing around strings and numbers.
.4. Why are you using Typescript and having your junior devs learning all the dynamic, loose typing. Teach them to stay in the typed world.
I'm actually being overly harsh here, I don't think Typescript sufficiently separates the two words so that you can avoid the other, this is likely the root cause of my mixed feelings. I need to learn how to get TS to help me rather than it providing guarantee I can use.
.5. If I utilize points 1, 2, 3 and 4 as a base. Point 4 is the only one that I would say hints at long term negative effects. Otherwise these issues read more like
'Why utilize Typescript when there are better alternatives. We don't use those because static typing is pointless.'
Put in point 5 and it is topped off with 'and you don't believe me even though I've used Typescript longer than you.'
I think static type systems have proven themselves over and over again. But we also have sufficient evidence that systems can be built without static typing with stability and reliability that an argument against static typing is strong.
As someone whose job it is to assure that stability, I don't want to be doing it for dynamic languages.
I never said not to check types statically. Never said static type checks are redundant. I said you don't need anything but JavaScript and tooling you're already likely to use with it (with or without TS) for that. And that you still need to learn the dynamic type system JS already offers to actually safeguard against wrong types at runtime. TS adds zero benefit, and a great deal of burden, for things you already can do without it (and still need to do with or without).
Your response to point 5, in the context of your other interpretations of what I actually said, is "You still don't really understand TypeScript, no matter how long you've used it. How could you not accept its greatness and inevitability if you did?"
Your response to 4 ignores 3. They can't avoid dynamic typing. They're transpiling to JS. It also, as is far too common among TS fans, ignores what junior devs are already expected to learn (and anybody who tells them to ignore dynamic typing needs to go back and re-learn JS - sans TS - themselves).
Your responses to 4 and 3 prove my point about 2.
Then I guess you'd better start doing all of your frontend in WASM (loaded with plain ol' dynamic JS, with or without TS, of course).
My intention for my summary of 5 was thinking it might help you see how the sum of your statements come together.
Thank you yor try to do the same, though I need some help connecting the translation. Namely my mistake seems to primarily stem from the assumption that you are not in favor of types.
I would say this assumption mainly stems from item 2, which I explicitly asked for help in understanding. Could I get further clarification on how that point isn't general to all type systems mentioned?
I'm against butchering JavaScript to behave like another language through the use of a "superset" (TypeScript, though I have the same issue with CoffeeScript, ClojureScript, etc.) because someone decided that, rather than take the time to learn JavaScript qua JavaScript, one should impose one's personal language preferences on all other devs involved.
In his original edition of You Don't Know JS, Kyle Simpson said two very important things when it comes to JavaScript:
To be clear, what I'm saying is:
This does not help me understand your second point, which I'm still interested if I was able to extrapolate the meaning of correctly or not.
Thank you for providing a clearer position.
I think it is interesting that you don't like that someone is trying to make Javascript usable for themselves, yet are aware that the environment does not allow for an alternative, WebASM still has JS boot straps and is a much more recent option (not fully ready) compared to all previous attempts.
I'm not sure exactly how I can further clarify this, other than to say "yes, you should check types statically, but you don't need TypeScript to do so" and "but you still need to account for runtime, which static type checking will never solve, and vanilla JavaScript already provides tools for this if you bother to learn its dynamic type system instead of acting like a static checking superset eliminates this need."
Making something useful for yourself is one thing. Imposing it on others and acting like it's the only way is something entirely different. Just look at all the demands on library maintainers to add types to their libraries, when the maintainers themselves haven't expressed any desire to use (let alone maintain additional support for) TypeScript.
I think I'm having a hard time with these two statements, why would you believe we should check types statically if at the same time you believe they do nothing for runtime. I think we both agree that the only thing that matters is runtime behavior. Everything else is done in support of getting the runtime portion correct.
I might be in agreement on pushing you desire for Typescript onto 3rd party libraries. I feel as though our discussion is more focused on why Typescript is or is not valuable and why it is winning the static typing challenge of javascript.
Both of these can be true at the same time:
In either case, TypeScript is a pointless "solution."
Why are you utilizing an absolute if you know it does do something, and you find that something valuable enough to us static type checking?
I'm not mentioning Typescript because your original statements seem very specific to static typing. That there where other options to Typescript were made in point 1 and I expected that was a different issue you had.
It's not either/or. Check/annotate your types statically, but don't pretend (as TS users all too often do, and as you seemed to indicate earlier in the thread by saying you didn't want to deal with dynamic types) that you don't need to check in runtime and that JS doesn't already offer the tools to do so.
That "does do something" is easily solved for with just comments (that TS advocates tend to label "a code smell," oblivious to the irony of adding tons of nonstandard syntax instead) to document functions and their types, which has been around in the vanilla JS world since long before TS and other "superset" "solutions" to typing, and ESLint (which you should be using in a modern JS development toolkit anyway).
The "other option" I'm recommending is the one which alters JavaScript the least (i.e. not at all, rather just implementing good authoring practices).
I AM focusing on TypeScript (because your article is in response to one against TypeScript and) precisely because its supporters act as if it solves things which are impossible in vanilla JavaScript (I've shown a million times in this thread that they're not) and that their resulting code is safer and better organized. It's not, and I'd invite them to see the long-term (as in multi-year) effects on code "organization" vs a similar well-commented (and still self-documenting) JS codebase. In addition to the far fewer LoC, the JS codebase will benefit from devoting the hours (amounting to months in a multi-year project) TS devs would have wasted on satisfying tsc (just to get their code to run) on actually validating in runtime (again, necessary with or without TS), with time left over to spare.
I don't have the background on JS or TS to grasp gaps I expect to be closed with types, my little experience suggests TS does not go far enough. And for that I can believe TS is not good.
Basically what you say here I have no argument against, but maybe some semantics.
Two parts you emphasize are "vanilla javascript" and "standard javascript".
And while I agree that Typescript is not standard/vanilla javascript, I think you might be conserned more on the native browser support.
Personally I would not consider comment annotation as vanilla javascript. As to if it is standard, I would need to do a in depth analysis of JS projects I neither have time or access to.
Linters are great and I hope that any project using vanilla JS is making use of both of these techniques.
i recently worked on a backend, there was a function that executes some sql query and
groupBy
the result. the function was used from 3 other places and we knew there was something odd. It was not wrong every time, just in certain situations.The project is mostly in typescript, (at least all code in
.ts
files) but that function hadany
as return type.after spcifying the type, typescript directly pointed to the actual bug we had.
So, while the typeScript typesystem is not perfect (still need validation libraries for real typechecking at runtime), I believe it is valuable to use it in projects.
Merge errors always are caught by
GIT. This makes the branch argument weak.
Introduction of Typing is a design decision. It should have already considered ensuring proper interface implementation.
Dynamic typing is nice but a potential liability. If we look at Graphql we see it requires type defs.
None of the issues cited have much to do with Typescript or Javascript. Its merely lack of knowledge of the all important interface of 2 systems; which should, at a minimum be documented.
Git only identifies content modification conflicts, it will not tell you the data no longer contains the property you are looking for. This relates more to private API were rigidity on defined APIs are more fluid, granted internal APIs can deserve the same rigidity.
I agree that the examples have system boundary issues, which is not language specific in fact mine was in C#.
People better understand the importance of defining their interfaces across systems, but like you did can easily dismiss the importance for internal interfaces as "git will catch it".
I think my git comment was in thinking of a new type in current branch where there are other non merged branches that didn't know about the new design change. I agree Git would never find that edge case.
The problem of 'it will not detect the property ' is true in Javascript and one pitfall that can cause headaches. Its the primary reason I prefer Typescript, where for example, it's impossible to misspell a property name. I have seen dynamic Javascript object property misspelling.
Yes, well defined interfaces are required but not always present. Any code like that is of poor quality.
Since my context is with test code, everything is internal. I've tried hard to use appropriate depreciation (is that even a thing in Javascript?) but I have had slip ups and it is nice to have a type system to keep things in check.
The soundness argument is weak given the example in the article. Why? Because the article clearly shows the mix of strong and dynamic typing, but then points out the compiler doesn't catch it. The real problem isn't the compiler. Its a result of mixing Typescript and Dynamic typing. Which in some cases is fine.
Maybe I misunderstood the soundness argument, I thought it was exactly what you stated. The type system is mixed with dynamic typing making it unsound.
Something that I think gets overlooked in types vs no types is that you need a typed language if you have very strict performance requirements. Rust code achieves the speed it is known for only because the compiler can choose the fastest set of instructions based on what it knows about the types.
Just because there are types doesn't automatically imply that there is optimization for performance - which there isn't in TypeScript.
2017
2020
That is probably why some teams want TypeScript's annotations to be compatible with Google's Closure Complier.
Annotating JavaScript for the Closure Compiler