It has its issues, but the best use case is as a mechanism for optional parameters, especially when adding new parameters to existing functions while maintaining API compatibility, which is especially important for standard functions.
If you want to reduce the amount of mistakes made, in TypeScript accidental under-application becomes a type error. I feel the experience of writing TypeScript is a lot smoother than JS alone.
When you add a new optional parameter to a function, there is no guarantee that you won't break code that calls the function.
Here is one notorious example:
[ '1', '1', '1', '1' ].map( s => parseInt(s) )
This returns the expected result:
[ 1, 1, 1, 1 ]
Then you think, "I don't need that little wrapper, I can just pass parseInt as the callback directly":
[ '1', '1', '1', '1' ].map( parseInt )
And this returns something that you may not expect:
[ 1, NaN, 1, 1 ]
That happens because parseInt() takes an optional second parameter with the number base, and map() passes a second parameter with the array index. Oops!
A similar situation can arise when you add a new optional parameter. You still have to check all the call sites, and for a public API you won't be able to do that.
This is true, I mainly didn’t bother mentioning it since it is still a fairly obscure edge case (relative to just getting started in JS anyways,) and because TypeScript catches that too, most of the time.
Presumably OP didn’t want to create a new function, or they would have. JavaScript supports default parameters, but again, presumably OP wanted the caller to supply all the arguments.
If you’re trying to change the signature of an existing function, I don’t think we’ve yet figured out a better safeguard than static typing.
It’s possible to use "arguments.length" to “change” the function signature and execute different code for different parameter counts. It’s a hack, but it works.
The first solution can be done in JavaScript and it is done sometimes, but other times it may be undesirable, especially for refactoring. The second solution was done; ECMAScript (since the 2015 edition) supports explicit default arguments.
When you add TypeScript into the mix, it can help a lot as it is often able to detect under- and over-application as type errors (though not always, due to intentional looseness in the language; TypeScript is not fully sound.)
If you want to reduce the amount of mistakes made, in TypeScript accidental under-application becomes a type error. I feel the experience of writing TypeScript is a lot smoother than JS alone.