You can refactor your entire codebase with a few operations and don't have to worry about anything breaking.
Is your timestamp in seconds? Millis? Is it a duration? Does it have a time zone?
Have you ever written a method that returns more than one type of thing? Or had polymorphic inputs?
Where are your dynamically dispatched calls even used, and how do you know you modified them all?
One of the most common operations in building new features is plumbing new state through the system. Types are there to help you in such perilous times.
Types are refreshing and reassuring.
People often say this, and I don't get it. What JS tests are you writing that become unnecessary in TypeScript? I've used a fair amount of TypeScript and plain JS, and end up with similar amounts of tests for each. With JS, I almost never want to verify only that a value is of a specific type; I want to look at its contents, which means I'd need to write the same test in TypeScript.
function doSomething(thing) { ... }
How many different possible representations of a 'thing' do you have? A json object? A class object with behaviors? A database id? Some sort of natural key like a SKU? A URL? Is it a metric or imperial thing?You need integration-level tests around every method call to ensure that caller and callee agree what kind of 'thing' representation to use. Type systems can eliminate this class of bug entirely.
1. The library makes an assumption on a type (in the case of Python, that's typically "there's only one type of strings", "there's only one type of streams", sometimes "there's only one type of numbers", etc.). Since the language is loosely-typed, there's no API barrier that checks this, it's all duck-typing.
2. Tons of code is written based on this assumption.
3. Assumption changes. In this example, this happened with the Python 2 => Python 3 migration, but it also happens once in a while with smaller version increments of either Python itself or Python libraries.
4. Where is the assumption used in your code? Well, if you're lucky, you're going to find out once a piece of your code throws an error because it's attempting to use a method that doesn't exist anymore. If you're not lucky, the behavior of your code has changed subtly.
In the loosely-typed world, the only way to avoid this is to have very strict boundary checks. Which means basically manually implementing subpar type checking at the borders and unit tests and/or fuzzing just to ensure that your mock-type-checking actually does its jobs.
Of course, you can often get away without doing this as long as your code is for internal use only. But if you're writing a library and if you want your users to be able to troubleshoot problems without too many difficulties, you're doing manually what the compiler is doing for you in a statically-typed language.
Metric or imperial integers? Your unit tests failed to prevent this $300 million blunder:
https://www.wired.com/2010/11/1110mars-climate-observer-repo...
But more commonly what is going to happen is that someone (two years from now) is going to change a 'person' parameter from legacy SSN to database id and some users are going to get "you don't exist" when they show up at the hospital to get medical service.
You're right though, most folks in the JS/Ruby/Python ecosystems don't do this kind of testing. It's a recurring joke:
https://www.google.com/search?q=2+unit+tests+0+integration+t...
As for frameworks in the languages you listed...
Ruby and Python: https://github.com/HypothesisWorks/hypothesis
Elixir and Erlang: https://github.com/proper-testing/proper
Node and JS: https://jsverify.github.io/
As for real world use-cases, imagine you’re writing a program that accepts timestamps as input and has to implement branching, requirements-defined business logic based off of them. When you’re writing your unit tests you can use the requirements to select timestamps that are “known good” and “known bad”, but it’s hard to explore this state space on your own.
Same thing goes for handling unexpected inputs to certain functions. You probably don’t want to check _every_ type of input for _every_ dynamic function, but it might make sense to make sure that certain “entry points” to your program fail in the expected manner when they get poorly typed input.
Sure, but you need those tests anyway to verify that your code actually works. I agree that type systems reduce the occurrences of some classes of bugs; I'm only disagreeing with the claim that they reduce the amount of tests you need to write.
I recently ported some (quite complex) code I wrote from JavaScript to Typescript. The code has about a 2:1 test to code ratio, and a fuzzer for correctness. While porting, I ended up adding a couple “useless” assert(typeof x === ...) calls to quieten the compiler, which felt useless because my code was correct. Lo and behold, the assertion tripped in my test suite - apparently I was sometimes treating a string as an object and didn’t notice. Which was a serious issue; and could end up being a security problem for some people. My fuzzer didn’t find it because it never occurred to me to add string method names in my random data generator.
Generally I find that the bugs that are easy to find with tests and the bugs that are easy to find with static types are different. You can eventually find all bugs with a sufficiently large test suite; and with enough PhDs you can apparently formally prove everything. But you get the best bang for buck with a little of each. A few tests is much better than no tests. But in the same spirit, I find no matter how big my test suite, there’s a good chance static types will improve my code.
Typescript is far from perfect, but I sleep better at night with a type checker checking my code.
I don't think we disagree much. As your experience shows, trying to replicate type checking via unit tests is almost always impractical. Which means the tradeoff of not having static typing is not that you're writing more tests, but that there's an increased chance of bugs.
And I agree that once a JS project is large enough to have a build process, the benefits of TypeScript almost always outweigh the costs. I'd even like to see JS interpreters allow and ignore TypeScript types, so you could use it without a build step.
Why would you design a function in such a way that one of the arguments can represent so many different things? The problem here has nothing to do with testing. The problem is that the function itself is poorly designed.
If anything, the difficulty of writing a test for such a function would in itself be an indication that the function needs a refactoring.
We're pretty deep down in abstract discussions here and I have no idea what your code looks like or what it does, but I think it's helpful to point out that knowing the type means you already know the contents to some extent.
The way I see it, checking if a value is what you expect is always good, but if you additionally get an error automatically because your number is now suddenly an e-mail address, or the function you wrote that expects phone numbers suddenly gets a name instead, in my mind that's "built in testing". It doesn't make other testing unnecessary but it sure raises the bar for cleanliness -- which is especially useful when you're working with other components that aren't JS where types do really matter (like databases, for example).
Idk about you, but when I’m sussing out the behaviors of a complex application it can take numerous iterations over different possible types and shapes of the data in order to come up with something both robust and performant.
The single greatest benefit of languages that afford static analysis is that, when I change the shape of some piece of data where it is defined, my editor lights up like a Christmas tree and informs me of every place in my code that was just broken! This is extremely useful for being able to quickly iterate on features. I don’t have to remember all of the calls sites dependent on some API. It allows me to confidently make (sometimes large) changes to my domain and know for sure which pieces of code might also need to be refactored.
I’ve worked in codebases where the confidence I’m describing above does not exist, and what happens is a lot of defensive programming and wasted effort (and time!) dancing around new implementation because no one wants to change anything. Changes tend to become “append only” (we can only add to the interface) because it’s hard to know what’s going to break if you actually change it. This can be okay most of the time, but sometimes new requirements... well... require new approaches.
The above is related to “testing” insofar as I don’t need to test where pieces of code depend on one another in order to know when their contract breaks. But the benefit is not really about testing at all. It’s about work flow. It’s about velocity. And it’s about freedom.
If such (refactored) method is being called from code that's not covered by tests, the bug may not be discovered until it blows up in production. With static typing it would be caught at compile time.
Well there's your problem :p
I agree that if code has no tests, it's more likely to be correct if it's in TypeScript than JS. But I don't consider that an acceptable bar, so if I'm doing your code review I'm going to ask for tests in either case. And once you have tests that verify the behavior of the code, you're not getting much incremental benefit from type checking.
No unit tests needed to show that it is balanced :-)
Usually what I do is that once a project has reached a point of maturity and the requirements merit it I move parts of it to a typed language (go). That’s for things that need to be both performant and maintenable long term without much effort.
I actually benefit from that rewrite in that I have yet another chance to review assumptions and address higher order
I don't understand what that means. How is TypeScript removing friction between JavaScript and tooling? That doesn't make any sense. TypeScript is a typechecker and a language that compiles to JavaScript.
I never have the class of bugs in my projects that people seem to laud about typing to solve. It’s kind of ridiculous to me and cumbersome and makes certain dynamic programming concepts difficult or impossible. I don’t want to give up power just because other developers are sloppy.
I want my code to be readable, and succinct. A lot of times that requires creating functions dynamically at runtime or some other such thing that would void the benefits of a typing system anyways.
I recognize that in working with a team where people have other styles, varying attention to detail levels and skill, they can’t all be trusted to produce bug-free code without the constant nagging and overhead of a pedantic type system.
My solution is not to employ another piece of technology to solve this problem, rather it’s just not to work with those people.
If you can’t write dynamic code without a bunch of type errors at runtime, learn to be a better programmer first before blaming the tools.
How many times has six-months-hence you cursed you for this attitude?
Be honest.
So you could be leaps and bounds better than me. Or you could be--let's be generous and call it "optimistic to a fault"--and retrofitting to a narrative about Good Programmers Don't Need Guardrails. And, TBH, I see a lot of people who are the latter and very few who are the former--and most of that very few tend towards statically or at least optionally typed languages by default. So forgive me if I don't believe you.
Will you in two years? Will your coworkers? Will the contractor who has no context for the code understand it?
Like variable names, types convey information that are crucial to the understanding of your application.
Under the hood, there are "types", even if they are weak, so you'll need to know about implicit typing anyway. Making them explicit is both a way to test correctness and a way to document your code.
Meanwhile: https://www.zdnet.com/article/microsoft-70-percent-of-all-se...
'Percentage of memory safety issues has been hovering at 70 percent for the past 12 years.'
And this is not just Microsoft–it's a pretty consistent finding across industry, that when programming in C/C++, memory safety issues make up around 65% to 70% of all issues.
What this tells us is that people in general can't guarantee the safety of their code, but an automated checker can.
And I suppose when those requirements never changed you never had to rewrite lots of these perfect functions or resort to “append only” programming in order to make sure regression was impossible instead of making large changes in place.
Forgive me, but it’s clear you have never worked on a program of any consequence, so it’s hard to read your comment and do anything but wish you the best while also completely ignoring its content.
It's like trying to convince experienced motorcyclists that you don't need a helmet because you're quite the driver. It's just defensive driving, people! That the helmet is some sort of crutch you've transcended, and maybe if everyone had a bit more skill -- like yourself -- they'd understand.
Yet it always seems to be half of the dynamically- vs statically-typed language debate.
That sounds fine, as long as no-one else will ever have to read or work on your code.
On these projects you often find yourself needing to work on higher levels of abstraction. Types help here by instantly telling you what the data is and providing hints.
Types take extra time to write and maintain, but they significantly reduce the difficulty of working on large projects.
Can you say the same about all your dependencies?
I and most people however are human and make mistakes sometimes. Worse, the bigger the project, the more you need to collaborate between people and sometimes teams. Types are almost like docs that check your code at compile time and reduce the chances of human error creating a production failure. Its not a panacea but its certainly nice to know that if I change a functions parameter, that the system breaks someone else's code depends on that function following a certain signature. At that point, I can either write a different function, message him to update his code or discuss how to make the changes together. All 3 cases are MASSIVE upgrades over fiding out shit broke in prod with some undefined behavior arising.
The only perfect decision I made is to accept that I make imperfect decisions, I wish I could attain the consistent perfection that you describe here.
For small projects, dynamically typed languages are fine. but as the project grows bigger, the static typing will help and will make thing simpler.
As the project grows, refactoring will be an issue. Static typing will make it simpler to refactor.
Tests are smaller.
Reading the code will be easier since you know the contract of functions.
Fewer runtime errors since most errors will be caught at compile time. Also, the stronger the type system it is, the more it will be caught at compile time.
I don't think it slows me down that much. It's a little more typing, and there is compile time overhead. In some cases, you have to work your way around the type system. But at least with Scala, I am confident my code will work as intended as long as it compiles successfully. I spend less time debugging in runtime and writing tests. I think static typing might save time in the long run but not short run.
Would love to know how your journey was. At first I was super hesitant, but now I am actually floored at the power Typescript has. The ability to tell the compiler to simply "trust me" has enabled me to build many abstractions I'd battle for hours to do in Scala.
I'm 100% TypeScript now and miss Scala. I miss using monads. But since Typescript doesn't have for comprehensions, using any of the monad libs for TS (like fp-ts) is messier than plain old TS. Also, Typescript doesn't have pattern matching, thus ADTs are more painful in TS.
But TS is a nice language. Instead of monads, they have stuff built into the language to make things easier and cleaner like null coalescing (compared to Option) and async/await (compared to using IO or Future). I can live with that. I've grown to like TS.
I'm curious what abstractions you had easier time with in TS compared to Scala.
I do miss the for comprehensions, but have built an abstraction that does the same thing for my purposes. It isn't quite as good, due to the whole monad thing, and me fitting it to the average Javascript programmer who won't want to learn functional programming all at once. So at its core it is pretty much async/await with sugar.
I quite enjoy exploiting structural typing for unit testing purposes. Gone is the need to learn a mock framework and its DSL; just write plain ol' JS for the parts you are interested in, and tell the compiler to chill. ;) Long term, of course, you should probably just have doubles.
Personally, I think structural typing systems are the path forward for functional languages, and that means accepting that Haskell needs to go to move on to a retirement home. I was pleasantly surprised by how well Scala did subtyping with ZIO's `Has` + variance annotations. Shame TypeScript is bivariant with function parameters.
I have seen my share of "innovations" come and go, but I too fail to see the empirical evidence that most bugs/errors are caused by type mismatch. In fact, I would even go as far as to argue that in my experience the benefits of typed languages (and the class of bugs they prevent) do not outweigh the usually larger code size (and often even additional complexity).
Maybe I've just tried the wrong languages, but I was never convinced of the great improvements usually promised. At the same time, I have seen other people twist and jump through hoops, while vigorously holding on to their conviction how all that is an improvement.
Sure, some errors will move from run time to compile time with typed languages. But if those are a systemic problem in your code, you probably have even bigger things to worry about (the ones you already keenly pointed out).
From a cynical point of view, I can understand why corporate entities would love typed languages (for the increased code size and complexity), but that's another story.
For the (pretty obvious) logical error of claiming that you have to believe that most errors are caused by type mismatch to consider stating typing beneficial.
A value of the wrong type passed forward because it's almost the right type can cause errors that make their way into production.
I hear you say "so unit test your code!", and to that I reply, we're a c# shop that has only recently started pushing some of our code to the client. If you can convince management that we should shift our paradigm to allow for the time to write tests that don't add much to the sprint we're in, I can send you their email address.
The fact is, a large portion of unit tests solve the same problems that strong types solve.
1.) The fact that I even got involved meant the issues were particularly hard to track down
2.) These errors seem to be more of a time sync within a larger team/project than many realize
Typically these were caused by either scope issues and/or some context object that's been riding dirty all day. In the case of the latter hunting down the code that did the naughty can be particularly soul crushing.
By having types, you open the door to formally describing the building blocks of your system. This lets you create very specific, well understood, pure pieces that then are composed together to bring about your desired functionality.
Why rely on documentation that a compiler cannot statically verify, as opposed to a type (annotation or otherwise) that can?
I think you're correct regarding your "most errors are caused by" comment, and no programming paradigm will ever get rid of these - you can always find a way to write bad code. And you are also correct that there are type zealots out there who create beautiful prisons they call abstractions. But at the end of the day, types are a tool, and while I'd highly recommend learning them, I also recognize some projects and teams will never make use of them.
If you eliminate a class of errors by doing so, isn't that still less errors overall?
Static types are documentation. Better yet they're documentation that can't go stale without a computer yelling at you that it's wrong.
> Static types are documentation. Better yet they're documentation that can't go stale without a computer yelling at you that it's wrong.
Absolutely, and I'd go further:
"Most errors, in my experience, are caused by bad architecture, poor documentation, and poor communications strategies between MVC, etc"
Good static types (a la Hindley-Milner) encourage good architecture, serve is documentation that cannot go stale, and encourage good communication strategies between different parts of a codebase.
"Pass in three parameters with a callback in the fourth position. The second param is configuration so pass in a string, but if you need to really customize stuff actually pass in an object instead and by the way if you don't have a third parameter then just chuck the callback there, why not!?"
Typescript is great in that somehow it is technically possible to write types for existing code written like this, but writing new code like that is cumbersome enough to prevent it.
"This is an array of strings, but it could be an object. If it's an object it will have this key and based on the name of the key it's this type. But it could also have or not have this other key and..."
ARghhhh! You can tell what kinda language the back-end was written in with that crap because while TypeScript can actually type most of that stuff it's a huge mess in something like C#, Golang, Java, etc.
The problem is, you'd have to have the documentation open to make the slightest tweak to anything. No help from the type system.
I don't agree with you. I don't think that loose typing is based on opinion, and I do not think it is because people who start out in statically typed languages are unable to "grasp" loose typing. I think we are seeing a people abandon loose typing because the industry has observed that loose typing is harmful to even small, a few thousand lines, projects.
Static typing catches bugs. It makes your code easier to maintain. It makes your code safer to refactor. It checks your code to ensure correctness. It provides documentation about variables. It reduces the amount of code you have to read in order to understand a specific piece of code. These are empirically good things that you do not get in loosely typed languages.
An untyped value does not carry any information or explicit assertions about the variable. Take a URL for example: A "string" URL does not carry any information about weather or not the url string is a valid URL.
In an untyped environment, only by understanding ALL upstream code are we able to make assumptions about downstream code. We know that the "string URL" is a sequence of characters, and nothing more.
However, if we have a type, we can have a "URL" type. Since we have a type, we do not need to concern ourselves with all upstream code, only the code associated with the "URL" type. So, instead of potentially thousands of places to seek information about our "URL" variable, we have only one: The "URL" type. We look at the implementation of that type, and we know weather or not the URL has been validated.
Types dramatically reduce the amount of code you have to read in order to understand a given piece of your application. This makes your application more secure, more readable, and easier to maintain.
Oh, wow, given this unnecessarily combative start, am I about to finally read conclusive proof that will stop that over sixty years old flamewar?
> I think we are seeing a people abandon loose typing because the industry has observed that loose typing is harmful to even small, a few thousand lines, projects.
People are not abandoning or adopting anything, both static and dynamic typing have existed for as long as computers have been a thing. Hell, LISP is older than pretty much every statically typed language still widely used, methinks.
> Static typing catches bugs. It makes your code easier to maintain. It makes your code safer to refactor. It checks your code to ensure correctness. It provides documentation about variables. It reduces the amount of code you have to read in order to understand a specific piece of code. These are empirically good things that you do not get in loosely typed languages.
Where's this empiric evidence of all these advantages you mention? I'm a bit shocked that I managed to miss it... And that so did half the industry. Such conclusive study would have ended that and the industry would have coalesced into this unquestionably superior type system you mention.
> An untyped value does not carry any information or explicit assertions about the variable. Take a URL for example: A "string" URL does not carry any information about weather or not the url string is a valid URL.
Can't do that with static languages either. Call it a type (or class), call it a parsing function, ultimately the result is the same. You need something to ensure that it is an url. Those exist in dynamically typed languages and are not obviated in static ones.
> Types dramatically reduce the amount of code you have to read in order to understand a given piece of your application.
Conversely, types substantially increase the amount of overall code one has to write.
These has never been a matter of opinions. The flamewar still rages after decades because what's the factually best type system hasn't been found. Hell, we might be likely closer to prove that that question has no answer.
In short, ultimately everything you said was just, like, your opinion, man.
In a loosely typed language you cannot have compiler-level enforcement that a variable has been produced by a factory function that ensures that the passed in variable has been parsed and determined valid.
Additionally in a loosely typed language you do not even get the OPTION of labeling a value as "validated/unvalidated", you only have one option: Pass a string, and expect the new developer to read upstream code to understand where the variable came from.
> In short, ultimately everything you said was just, like, your opinion, man.
Is it my opinion though? Or did I pull it straight out of Building Secure and Reliably Systems, a book written by Google engineers to express industry best practice for developing software at a scale?
Hundreds of books have been written on the matter, and the results are still inconclusive.
> Is it my opinion though? Or did I pull it straight out of Building Secure and Reliably Systems, a book written by Google engineers to express industry best practice for developing software at a scale?
Yes and yes, you happen to share their opinion.
Now, should we pretend that appeals to authority turn an opinion into fact now? If so, to whose authority should we appeal? Theirs? The different google engineers who work in more dynamic languages? Hell, designers of dynamic languages who have worked at Google? The people who designed and work in LISP or any of its derivates? Whose authority weighs more?
Or maybe we should acknowledge that appeals to authority hold no real decisive value in deciding something like this, and instead contribute to studies trying to gauge how effective different type systems actually are.
Except that even those turn into flamewars: https://www.hillelwayne.com/post/this-is-how-science-happens...
> Conclusion This paper described an experiment comparing static and dy- namic type systems for programming tasks in an undocu- mented API. We gave 27 subjects five programming tasks and found that the type systems had a significant impact on the development time: for three of five tasks me measured a positive impact of the static type system, for two tasks we measured a positive impact of the dynamic type system. Based on the previous discussion, our overall conclusions for the use of static/dynamic type systems in undocumented APIs are the following: 1. There is no simple answer to the static vs. dynamic typing question: The question of whether or not static types are helpful for developers cannot be generally an- swered without taking the programming tasks into ac- count. In fact, this is completely consistent with the results of previous experiments, such as Prechelt and Tichy’s [22], or our own experiments[10, 28]. 2. The type system has an influence on the development time: The choice of the static or dynamic type system had an influence on the development time for all program- ming tasks in the experiment. Again, this is consistent with previous experiments (such as [11, 22, 28]). 3. Dynamic type systems potentially reduce develop- ment times for easier tasks: Although we are currently not aware of how to determine exactly what “easy” and “hard” means, it seems that if a dynamic type systems has a positive impact, it is for easier tasks (which is consistent with the experiments described in [10, 28]). Although there was one counter example in the experi- ment (task 1), we think that the result for this task is a consequence of the chosen subjects’ low familiarity with the dynamic language, Groovy (despite the presence of a warmup task). 4. Static type systems reduce development times if (a) the type annotations explicitely document design de- cisions, or (b) the number of classes to identify in the programming tasks is relatively high.
https://www.researchgate.net/publication/262317340_An_empiri...
Given the challenges to getting a good result identified in the study how do you think it could have been done better today?
And, of course there are exceptions in both directions for all of findings 2, 3 and 4; and those exceptions would also contribute to 1.
It's for findings like those that I wish people stopped treating type systems like religions. And paradigms, for that matter.
This is true, and confirmed by the TypeScript team
https://stackoverflow.blog/2020/06/15/talking-typescript-wit...
> Do you remember why the team came up with TypeScript, why you wanted to release something like this?
> A: When I joined the team, there were a lot of people at Microsoft who wanted to develop JavaScript at what we call “application scale.” Teams like TFS and Office wanted to build large JavaScript applications. A lot of those people had familiarity with statically-typed languages— C++, C#, Java, that kind of thing. They wanted to have that static typing available both for conceptual scalability and for the tooling.
> The language experts at Microsoft looked at the situation and said, well, we could try to write a new language like people had done before. There are projects like Script#, which takes C sharp and turns it into JavaScript, or languages like CoffeeScript that said, what if JavaScript had a different syntax? But what they decided instead was to just take JavaScript and add static types on top of that.
> At the time, JavaScript was experiencing a renaissance of growth, where they were adding features to the language again for the first time in a pretty long time. People wanted to use those cool new features, like arrow functions and classes, and they didn’t want to wait for all browsers to adopt them. They wanted to be able to use those features right away. I think those were the three things that TypeScript offered upfront: static typing for error finding purposes, leveraging that static type information for tooling, and providing the JavaScript features of tomorrow, today.
Seriously?
> They wanted to have that static typing available both for conceptual scalability and for the tooling
Sounds like they understood the loose nature pretty darn well.
TypeScript adds additional type checking at transpile time to code. The assumption that the people who can write code with these more rigorous checks can't write the same code with it all removed ... I would examine that a little more closely.
It's the same BS as Python and JS, the lack of typing but fundamental nature of systems that require data to be typed means half my code is checking that everything is properly typed!
Dynamic types have their uses, but in general they're more trouble than just writing typed code to begin with. And when your type system is more a suggestion than anything it provides little benefits.
On the contrary, as somebody who learned programming with a mix of static and dynamic types, I felt like TypeScript showed me what a good type system was capable of in giving me the best of both worlds.
Instead of being pedantry, I felt like the type system was actually on my side for once and helping me to write correct code with fewer unaccounted-for corner cases and unexpected runtime crashes. And it does that while avoiding the feeling I got from previous statically-typed languages I used where the type system felt like a straight jacket incapable of expressing non-trivial concepts and relationships.
I use Typescript everywhere I need Javascript these days, it's a breath of fresh air, and my code feels maintainable, tractable, and much easier to refactor.
They like TS a lot. And it’s obvious why. Having said that, I know no “old” programmer who used to prefer a different paradigm to express a wish to switch to TS.
Rich Hickey put it well in this quote “ Functional programs are desirable because they are simpler and easier to reason about, due to being mathematical i.e. they are free of time and place.”. I agree.
I mentioned that paradigms of functional programming languages like Clojure translate well to JavaScript. They do not to typescript, because it is a statically typed language.
If you are interested in why are you dynamically typed language has benefits for functional programming, please see this talk: https://youtu.be/oytL881p-nQ
Typing is optional both for individual variables via the `any` type, and compiler-wide, by configuring it to assume untyped variables are `any` by default.
I disagree, though. Instead of going with a lengthy list of why I think TS is great and disproving your arguments, which I assume everyone else will do, I'll admit that types can be hard, and so doing them right, or even doing them as best as one can, will always take some time. For a small project or a small team, that time might be hard to justify. But I think this is resolved with a good understanding TypeScript.
Even so, TypeScript might not make sense for many projects or small teams, but I find it hard to defend that TypeScript won't be a net win for any medium team or medium-size project.
This was my believe before, but I've seen multiple teams go from JS to Flow or TypeScript and the result has always been, when there's expert guidance, extremely positive. Productivity goes up, more bugs are fixed because there's more confidence in the safety of the changes, more people feel confident to work on any part of the codebase (vs just the people that originally built it or have worked more on it), and the defect rate dropped dramatically.
All it takes is once and you'll become a statically typed language evangelist.
People who use types regularly feel the same way about typed languages. Types don't test as many things of unit tests of course, but feedback you get on what they do test is immediate - literally as you type in a IDE. The speed of that feedback is important. It's a well know in quality circles that errors discovered later cost far more to repair. In the case of types in makes you don't write a hundred lines of code under the expectation a type provides method foo(), only do find out later the foo() provided takes an argument argument you don't happen to having lying around.
While payback for types over unit tests is smaller, the effort required to use types is at least commensurately smaller, and the amount of typing required is way smaller, particularly now we have type inference. Also in my experience unit test's need to reach every line of your code forces some pretty tight constraints on how you structure it. Who hasn't had to refactor some block of code so you could test some obscure corner case catering to an unexpected error? Types don't require that - the compiler always sees everything. None of this matters much if you are going to write unit tests anyway of course, but no one does that for small projects. This is another advantage of the type system - it's always there, even for 5 five liner.
Finally, types come with an added bonus. They give the compiler more information to work with, which means the compiler can make programs run faster. Rust talks about cost free abstractions, which the extreme case: the compiler knows so much about what you are doing it generates no code at all. In rusts case it rarely has to check for if a pointer references null before accessing, and greatly reduces the number of array bounds checks, has no garbage collector, and the pattern of a function checking the number and type or argument in order to implement overloads just disappears - along with the runtime overhead of doing the checking.
All of this is possible with jsdocs and flow but they are not nearly as powerful as ts and often times verbose. I don't like polluting the comments.
You don't need to use typescript everywhere although the popular zeitgeist will tell you to avoid any or ts-ignore. It's fine to write some parts in ts and others in js. Think of ts as eslint, jsdocs or flow. You don't need to be religious about it. Take its benefits wherever you can or do you think all the other tools are useless as well?
Ultimately, for any serious work, especially enterprise level work, your team members may include people of different calibers.
In such a case, where you have to work with code written by other members of your team, it becomes a serious problem to develop quality code.
For lone developers, it should not be a problem. But in enterprise settings, I think that is the reason why Node is not popular.
Compile time errors are much quicker and cheaper to detect and fix than runtime errors.
It just happens to be that I have never been exposed to the circumstances that would make me re-evalute TypeScript merits.
Accidentally mixing incompatible types is mostly a problem for junior developers. I'm much more likely to accidentally put on my pants, socks and shoes in the reverse order in my daily life than mix up incompatible types in my code.
The solution is not to add stickers all over your clothes to remind you which way is the front or what order you need to put them on; you don't need more tools, you just need more focus.
TS feels like something that was created to lure programmers who couldn’t wrap their heads around JS loose nature. Almost like it was created to convince Java and C# developers to use JS.
It have never felt about it like something that would make my code better or more organized. It definitely slows me down with very little benefits in return. (Again, can’t stress enough that this is a personal opinion based on personal use cases)
I appreciate that it forces me to be more intentional about organizing my code but I also get that when I use frameworks.
Of course I also appreciate when I can identify that an error is coming from a type mismatch, but as I said I learned to program in loosely typed languages first, so I never created that concept of defining types in my head. I’m always extra-aware of types mismatches in my JS code and is usually the first thing I check when something goes wrong. But I have never felt the need to have a way to explicitly define the types that I’m working with. I honestly fail to see the benefit when a large amount of code that you have to interact with (libraries and such) was written in classic JS.
Once again. Probably unpopular opinion, so I don’t mind the downvotes.