I don't mean this dismissively but Typescript's type system is insanely flexible, and _should_ let you do basically anything you want. Especially when you look at union types, sum types, type guards, the `never` type....
As a bonus, if you are willing to generate type definitions from other sources (like your backend code) you can go super far.
I do agree that the current `@types` solution without versioning isn't very great. Though I've found that for libraries without good definitions, hacking out a simple interface for the layer you use is good.
Just doing interface SomeLib {}; declare var lib:SomeLib; and then filling in the errors can be pretty quick in most projects.
It makes sense to use libraries, but writing up an interface definition for a library you use is usually quick enough.
I'd love to hear about cases where type definitions really messed you up.
Properly type lodash or ramda's curry in TS. All the versions I find basically make 5+ overload and a sloppy fallback. Flow can actually type it correctly but so few people know, even the flow typed version doesn't do it (last I checked).
There is also plenty of awkward places. Take the Apollo React HOC which can be used for query or mutations. It only achieve this by giving you an object that can be one or the other but you have to know which one you're getting.
Another common example is the redux case. You have the store which is of a type. The HoC gives you a function that takes an argument of that type. Nothing links the two though because it wasn't designed with types in mind, so you have to specify the type yourself (essentially a pinky swear). If it was designed with types in mind, the HOC/connect function would be created from the store, thus providing proper type safety.
Examples like this are pervasive. You can "type" most things, but in many cases you have to rely on sloppy methods that look type safe but really aren't.
If you write stuff with TS in mind from scratch, and use strict mode, it's another story. The type system is quite good.
I've had the same experience as you did. When there's no type definition available, starting from an empty object quickly resolves a lot of the issues. But that really depends on the complexity of library you're trying to declare.
On the other side, I've also had cases when the type definitions were a pain. We have a web app and use .Net/TypeScript/React. We also use the commercial Kendo UI version. Since there's no versioning available (and the Kendo UI team doesn't seam keen on "type correctness"), we are (almost) always using a type definition file for another (newer) version of Kendo UI. We've had to "patch" the definitions on several occasions.
As a bonus, if you are willing to generate type definitions from other sources (like your backend code) you can go super far.
I do agree that the current `@types` solution without versioning isn't very great. Though I've found that for libraries without good definitions, hacking out a simple interface for the layer you use is good.
Just doing interface SomeLib {}; declare var lib:SomeLib; and then filling in the errors can be pretty quick in most projects.
It makes sense to use libraries, but writing up an interface definition for a library you use is usually quick enough.
I'd love to hear about cases where type definitions really messed you up.