> Over the years, the type system of TypeScript has grown from basic type annotations to a large and complex programming language.
Give someone (particularly a developer) the opportunity to build something complicated and undoubtedly they will. So now you have two problems, the complicated program that actually does some hopefully useful work, and another complicated program on top of it that fills your head and slows you down when trying to fix the first complicated program. You may say 'ah yes, but the second complicated program validates the first!'. Not really, it just makes things more complicated. Almost all bugs are logic bugs or inconsistent state bugs (thanks OOP!), almost none are type bugs.
However, static analysis of existing code (in Javascript), without having to write a single extra character, may well have great value in indicating correctness.
Edit:
> TypeScript's type system is a full-fledged programming language in itself!
Run! Run as fast as you can! Note that this 'full-fledged programming language' doesn't actually do anything (to their credit they admit this later on)
Edit2:
> [...] is a type-level unit test. It won't type-check until you find the correct solution.
> I sometimes use @ts-expect-error comments when I want to check that an invalid input is rejected by the type-checker. @ts-expect-error only type-checks if the next line does not!
What new level of hell are we exploring now??
I am genuinely afraid and I'm only halfway through this thing. What's next? A meta type level language to check that our type checking checks??
Hard disagree. For me, the proof is in the pudding. When writing regular Javascript, I need to run and debug my code often as I'm developing it to make sure all intermediate states make sense and that there's no edge cases I've missed. With Typescript I can often write code for hours without even starting it up, and when I finally do run it, it usually works correctly right out of the box.
I recently started working on a project with typescript for the first time. I have been astounded by how often my code works the first time I try it because so much is caught by the typing system. I know what I want my code to do, how it should do it, and that it does work - my errors are almost all from mistyping things.
Massive productivity boost, and I have a kind of confidence in my code that I never have had before, not having used a strongly typed language before.
The core functionality of Typescript of adding basic type hints to JS is definitely better than writing plain Javascript, but the type system feature set is way beyond it's peak and deep in diminishing returns territory. I realize that this complexity may be required to gradually convert a 'worst case' Javascript code base into 'correct' Typescript, but IMHO most new Typescript code should limit itself to a simple subset of the type system.
I've been coding C++ most of my life, and I must say, TS is starting to look more and more like C++ (which definitely isn't a good thing because it lures programmers into complexity).
It’s certainly nice that you know your code will still run when any of your values are null or undefined, and you are forced to deal with that scenario.
There are a depressingly large number of people who just don't get static typing. I think a lot of them use very basic editor setups - think Vim or Notepad++ so they don't see half of the benefits.
The mental gymnastics I have to engage in to work on large JS projects without TypeScript is unbearable. I have to switch between the two often and it’s night and day.
Have you worked with typescript? Adding strict types to JavaScript fixes pretty much all the complaints I use to have when working with frontend services/clients.
Typescript isn’t a “now you have two problems” anymore than types in any other language are.
Maybe in a controlled environment Typescript can produce benefits - however much of my argument is that our environments are typical uncontrolled, let's not give the monsters any more magic than we have to.
I’m inclined to say that any magic applied here is a win. Making things complicated with Typescript is simply hard enough that those people that’d mess things up in the first place wouldn’t even consider trying.
Writing simple types with Typescript is simple, and improves any Javascript code.
Writing complex types (inference, generics, inheritance) is really punishing. And people that don’t truly know what they’re doing won’t even try (or at least in my experience, I’ve never seen them try).
It's the dreaded "I know enough to be dangerous" type that you have to worry about; the people who don't actually know all that much but enough to make a mess of things. I was brought in to help for a while on a project where they had compile times up at 4 or so minutes, if I remember correctly. The solution in the end really was just "Write dumber types", because their types were completely overboard and written just well enough to work.
A big bulk of their compile time came from things that could've been checked in much more efficient (for the compiler) ways.
I would never urge anyone to not use statically typed languages, mind you, I think people just need to be a bit more pragmatic. Sometimes I find it unfortunate that TypeScript provides the facilities it does while still not having solved the basic ergonomics of types (more consistent inference, etc.). Having types that generate types creates problems that I honestly don't experience anywhere else and I would rather that people just not in general, but that's something you can fix with rules.
An ironic part of all of this is that Haskell's type system is a lot easier to use and use well than TypeScript's, in the end, which is especially funny considering all the talk of pragmatism.
> Almost all bugs are logic bugs or inconsistent state bugs (thanks OOP!), almost none are type bugs.
Most, possibly all, inconsistent state bugs and many logic bugs are type bugs with a sufficiently-expressive type system properly used. That's why type systems have progressed from basic systems evolved from ones whose main purpose was laying out memory rather than correctness to more elaborate systems.
> Almost all bugs are logic bugs or inconsistent state bugs (thanks OOP!), almost none are type bugs.
Many bugs of these classes can be avoided with a sufficiently expressive type system. There’s a reason that Haskell programmers say if it compiles, it probably works correctly.
Ah but Haskell was built that way from the beginning, and has important invariants (pure functions etc) that make it possible to produce inherently sound programs. Of course none of that will help you make the right inherently sound program.
> Almost all bugs are logic bugs or inconsistent state bugs (thanks OOP!), almost none are type bugs.
With a sufficiently powerful type system (and typescript is basically the only non-functional language that makes the cut here) these aren't all that distinct. But even in codebases that don't take advantage of that power, this has not been my experience. I recently converted about ten thousand lines of legacy javascript to typescript at work, and discovered several hundred type errors in the process. State bugs also slip through pretty often, but we almost always catch pure business logic errors at code review.
Complexity or not, it's incredibly useful. To me the development experience is just superior, you feel more in control. No one likes building the app only to see "Cannot read properties of undefined" and then needlessly scratching head what's wrong. I have done a large refactor recently and it was such a breeze, with TS pointing out pretty much everything that needs to be fixed to complete it. Coding new features and sometimes it just works after the first build. TS is obviously the inevitable future since it offloads the stuff that the computer can do better and frees up the mental energy for the more creative stuff. The code is also more readable, when the types mean something there's less need for describing function parameters for instance, since you have a complete type definition of what that param represents and any comments made on those types are re-usable across functions.
A simple trick with plain JavaScript is to give all arguments default values. That gives a pretty good insight for anybody reading the code as to what "type" of arguments the function expects.
If you test-call such a function without arguments you will then know what kinds of values you can expect it to return.
The argument default values can not be inner functions but they can be any function that is in scope. Or if you are using classes it could a reference to any method of 'this'.
Then add some asserts inside the function to express how the result relates to the arguments. No complicated higher-order type-definitions needed to basically make it clear what you can expect from a function. Add a comment to make it even clearer.
> That gives a pretty good insight for anybody reading the code as to what "type" of arguments the function expects.
If you test
Only true if you're using very simple types, i.e. number and string. But "string" is pretty close to "any" and doesn't give you much info. If my function only expects two or three possible strings, it should be typed to only take those ones.
Comments are not a solution for much of anything, btw, and they only "work" if you read them. How many comments are in your node_modules folder?
> Comments are not a solution for much of anything,
What are Unix man-pages but comments about the APIs they describe? Are you saying you would prefer to replace them with the TypeScript type-language?
As I see it TypeScript is a a solution to the problem of how to describe a function, what it does, what it expects from its arguments and what it returns. That information can often be clearly and simply expressed with a comment, rather than with a complicated type-declaration. And type-language only describes the syntactic behavior of a module, not its semantics.
When type-declarations become more complicated than the code they are describing I think we're at a point of diminishing returns.
The other purpose of type-declarations is to catch errors. But if a declaration is very complicated how can we be sure there's no errors in it?
> What are Unix man-pages but comments about the APIs they describe?
Documentation. Obviously not the same thing as inline comments in the code. You can generate documentation using comments (i.e. jsdoc), but comments are the weakest form of guidance for other developers. Types don't replace documentation, but are part of the same goal: making code easier to consume.
> When type-declarations become more complicated than the code they are describing
Do you find this happening to you often? JavaScript is a very permissive language, and most JS devs learn to write code in a way that is difficult to type. That's a part of the learning curve of the language. Part of using TS well, is realizing that complicated types are a smell for complicated behavior, and modeling your data in a conceptually simple way.
It's not just about labeling everything string or number, but making it impossible to use the code the wrong way.
> But if a declaration is very complicated how can we be sure there's no errors in it?
Simple. You test them. Same as any other code. How do you test your comments?
Sometimes, but as a last resort. I'd prefer types, tests and readable code over comments, and if I feel the need to leave a comments, I usually see that as a code smell
Agree for the OOP part, most devs doing the transition from Java,C# to TypeScript are using the tool to port what they learned and apply it to a JS project and projects like Angular are encouraging this by enabling experimental stuff like decorators, but missing the real point behind TS, the type checking system which is to be fair, really awesome.
Yes, there is a huge difference between type checking a minimally type-annotated program, and dreaming up a rich type system just because you could (I should know, I used to be a Java dev).
You're giving yourself away by making that comparison - your experience as a Java dev gives you minimal insight into the expressiveness or utility of TypeScript's very different, much more powerful type system.
It's not about the expressiveness or utility - it is about types you write (bad) vs types the type checker generates and maybe shows you in an IDE (good).
The point being that people should write actual code, and should not write type code, simply because given an opportunity to write things, people will indeed write things, whether actually helpful or (more likely) not.
Give someone (particularly a developer) the opportunity to build something complicated and undoubtedly they will. So now you have two problems, the complicated program that actually does some hopefully useful work, and another complicated program on top of it that fills your head and slows you down when trying to fix the first complicated program. You may say 'ah yes, but the second complicated program validates the first!'. Not really, it just makes things more complicated. Almost all bugs are logic bugs or inconsistent state bugs (thanks OOP!), almost none are type bugs.
However, static analysis of existing code (in Javascript), without having to write a single extra character, may well have great value in indicating correctness.
Edit:
> TypeScript's type system is a full-fledged programming language in itself!
Run! Run as fast as you can! Note that this 'full-fledged programming language' doesn't actually do anything (to their credit they admit this later on)
Edit2:
> [...] is a type-level unit test. It won't type-check until you find the correct solution.
> I sometimes use @ts-expect-error comments when I want to check that an invalid input is rejected by the type-checker. @ts-expect-error only type-checks if the next line does not!
What new level of hell are we exploring now??
I am genuinely afraid and I'm only halfway through this thing. What's next? A meta type level language to check that our type checking checks??
> 3. Objects & Records
> COMING SOON!
> this chapter hasn't been published yet.
Thank God, I am saved.