That's not my experience, sadly. We switched from Javascript to Typescript over a year ago, on a fairly new project, but it mostly results in more errors that need to be fixed, that wouldn't have been errors in Javascript.
Being able to specify interfaces is absolutely nice, but overall I'm not convinced it's worth the trouble.
It's mostly a lot of extra boilerplate that's suddenly required. We're using Vue, and every time we write a method or computed property for a component that uses the this pointer, we need to pass it (this: any) as parameter. Any, because every component is different, has different properties, and it's constantly changing, so writing interfaces for those isn't worth the effort, since they only call their own methods anyway. Forget it, and it might still work fine locally, but the build server complains, so we have to fix it.
Most of the functional errors will be caught either by unit tests or by functionality noticeably not working. These are not things that would be caught by Typescript anyway.
The irony is that we're using typescript in the front-end, where it mostly gets in the way. I think Typescript would have been more useful in the backend, but we're not using it there, because originally our backend was trivially simple. Now that the backend is becoming bigger, I can imagine Typescript would be more useful there.
It could be that Typescript doesn't work well with our version of Vue. (I think the latest version is designed around Typescript which will hopefully make the process a lot easier.)
> These are not things that would be caught by Typescript anyway.
In my experience working with React, which is pretty heavily invested in typescript these days, if you go reasonably deep on doing typescript interfaces, it's like a switch gets flipped.
A light dusting of typescript really does barely anything; it's just boilerplate. But once you get up to about 80-90% coverage, it's like a switch gets flipped. All of a sudden it's really, really good at detecting discrepancies - I had a thing I was working on today, where I had a cute little svg icon component in the giant SPA program we're writing - I was just reusing the thing, and attaching a click handler to it, and all of a sudden - this component we hadn't touched in months, typescript starts griping about it. And I'm like "oh come on, this is so basic - what the hell could be wrong about passing in a simple onclick handler?" Well - turns out nobody had ever needed to use the "event" param on that function, so it didn't even use one internally - what I was passing in, in plain JS, would have just been thrown away, because the internal 'passthrough' version of the function had no parameter at all. And I didn't notice it in light testing (we have TS set to emit our program even if it's failing tests). I tested the component, and because the behavior's invisible/internal, it seemed like it was probably fine. Maybe I would have caught it with really earnest, aggressive testing later, but I didn't even need to - typescript just nailed it instantly.
I've had the privilege of working on some game development stuff outside of a web stack, and holy smokes does working in a complete, algebraically typed language change everything. When you go from 80-90% type coverage, to "hard 100%", it's just a complete 180°. It's just _freaky_ how good it is at catching errors. I'll change one little thing, and it can tell me "oh yeah - you know that cutscene an hour into the game? Yeah, you broke that." It's uncanny. It just absolutely changes everything about how I work.
Yeah, I guess at least part of the problem is that we're using a version of Vue that's not natively designed around Typescript. There is a patch for it, but it's not that thorough. Half-hearted typescript doesn't work. A version of Vue that assumes you're using typescript and has interfaces defined for everything, would probably make a massive difference, and I think that's what Vue 3 does, but we're not in a position to migrate at this moment.
Basically, any use of `any` should be avoided. Once you tolerate one `any`, you're on the way down.
One thing that I really, really do like about typescript is that you need to be explicit about whether a value can be null. Java lacks that, but the difference between `foo: string` and `foo: string|null` is stark.
Typescript compiler can be configured to silence many kinds of errors, these are especially useful while migrating a large Javascript codebase. Also look at typescript not just from an error catching perspective but also from a tooling and documentation point of view. With types at hand, modern ides work much better and reading types often helps in understanding the code. All that said if the project is small and more of a throw away with only a few members contributing code typescript may not add benefits.
There are some "cute" uses of truthiness checks that work in JS (and, in some cases, weren't flagged as errors by earlier versions of TS) that are probably a bad idea and are trivial to render more-explicit, so better, but do technically run OK. Example: "if(obj.some_method) {obj.some_method();}". Not an uncommon form in JS in the wild, but TS (correctly) flags it as a problem.
Otherwise I dunno what this could be. Especially what TS could be disallowing that'd be all of: valid in JS, a good idea in JS, and especially time-consuming to fix.
Being able to specify interfaces is absolutely nice, but overall I'm not convinced it's worth the trouble.