> Slow debug cycle due to increased compile time. If using TDD, it takes longer to run tests. The delays adds up - Especially on large projects and especially if you're not used to having a build step during development.

It is a bit slower. There is tooling to mitigate this (filesystem watchers and such), and I've only found it to be a big stumbling block on relatively large TS projects, but you're not wrong. In almost all cases, large projects without some sort of build phase are probably pushing incompatible and/or slow code.

> Sporadic source mapping issues/bugs which makes it hard to fix things (I had an issue with Mocha tests last week where it was always telling me that the issue was on line 1). I've worked with TypeScript on multiple projects across different companies but every single time, there has been source mapping or configuration issues of some kind.

That's not TypeScript's fault, in my experience. It's usually because of a relatively byzantine build system that's in charge of generating sourcemaps. In fact, I've never seen sourcemap bugs that were directly caused by TS (i.e. due to Babel/Webpack misconfiguration). YMMV, of course.

> Type definitions from DefinitelyTyped are often out of date or have missing properties/methods. It means that I can't use the latest version of a library.

This is the criticism I agree with the most. Things are improving as people start developing in TS more and shipping typings with their projects as first-class citizens, but it has a long way to go (PDFJS, looking at you).

> Third party libraries are difficult to integrate into my project's code due to conflicts in type names or structural philosophy (e.g. they leverage types to impose constraints which are inconsistent with my project requirements).

Not unique to typescript at all. If you have a promise/async-based control flow and you integrate a library that is all about fire-and-forget with eventEmitter subscriptions, it's going to look weird to integrate. If a library you import uses a different naming scheme, or a very different pattern (e.g. heavily chained single-purpose methods vs. a more declarative style with big config objects), it's going to cause friction. In TS, most of the typing/naming friction I experience is caused by modules that would have caused friction in pure JS as well.

> Doesn't guarantee type safety at runtime; e.g. If parsing an object from JSON at runtime; you still need to do type validation explicitly. The 'unknown' type helps but it's not clear that this whole flow of validating unknown data and then casting to a known type adds any value over regular schema validation done in plain JavaScript.

"This toaster doesn't make me coffee! Before I got it, I couldn't make toast or coffee. Now I just can't make coffee. What a terrible toaster!"

> Whenever I write any class/method, I have to spend a considerable amount of time and energy thinking about how to impose constraints on the user of the library/module instead of assuming that the user is an intelligent person who knows what they're doing.

That's probably the single biggest benefit of using a type system.

Many folks think the biggest benefit is the compiler rejecting code which violates type rules and thus prevents bugs. I respect that, but I think that's in the second spot after "type systems make you actually think about your data transformations beforehand". This feels annoying at first, and still feels annoying when organically growing/prototyping brand new stuff in an exploratory way. But man does it pay off in a hurry: once your project becomes just "small" instead of "tiny", you'll be glad you (were forced to have) thought about those structures as a whole rather than as problems came up.

> Compiler warnings make it hard to test code quickly using console.log(...); I'm more reliant on clunky debuggers which slows me down a lot and breaks my train of thought.

I'm not sure what you mean. If you mean "my code doesn't compile so I can't run it and printf debug", then see my response to your point above this one: while that may feel less superficially satisfying, it's saving you time and hassles in the short and medium (as opposed to immediate) term. If you mean "console.log causes the compiler to emit a warning or error", you can likely turn that off (e.g. no-console).

Regardless, I'm with you in the spirit that printf debugging should be easy and that you shouldn't be forced to use a debugger.

> The 'rename symbol' feature supported by some IDEs is nice, but if a class or property is mentioned inside a string (e.g. in a test case definition) then it will not rename that; so I still have to do text search after doing a symbol rename and manually clean up.

Sounds like an issue in the way you're writing test cases. This is just as much of a pain when refactoring highly dynamic pure JS. If something relies on stringy descriptions of objects/properties its interacting with, it's usually the last thing to be caught in a refactor and the first to produce bugs. Just say no to pointless metaprogramming.

> It takes a lot of time think of good names for interfaces and they are renamed often as project requirements evolve. It's not always clear whether similar concepts should be different types or merged into one. It often comes down to what constraints you want to impose on users; This adds a whole layer of unnecessary mental work which could have been better spent on thinking about logic and coming up with meaningful abstractions which don't require arbitrary constraints to be imposed.

This is the same as your above point, "Whenever I write any class/method, I have to spend a considerable amount of time and energy thinking". It's a different paradigm than the immediately-satisfying-feeling you get from writing little bits of code and seeing them immediately "work" for a given test case. It takes some getting used to if you like the feeling of frequent, steady, incremental progress. But after trying it for awhile, I bet you'll find that you've developed features faster and feel more confident that they'll behave correctly in unexpected situations. You also may find that, if you think about the typing beforehand, when it comes time to start "thinking about logic and coming up with meaningful abstractions" you'll find those things much easier to do if you don't have to think about e.g. nullability edge cases while you do. Personally, I find that it orders my thinking into a "How do I constrain and/or generalize what the behavior must be" phase and a "now define the behavior for this specific constrained type" phase, which is very useful--especially in heavily-generalized systems like form builders or otherwise user-customizable display editors.

> Setting up TypeScript is a pain.

Sure is. It's gotten a lot better, but has a ways to go yet.

> Adds a lot of dependencies and complexity. It's a miracle that TypeScript works at all given how much complexity it sweeps under the carpet. Have you looked at the output JavaScript? Try to compile something that uses async generators and see what the output looks like.

If your TS project is big enough that you're suffering from compile times as you mentioned above, you probably use (or should be using) a build step. I've never seen a JS polyfiller that doesn't create insane code when presented with things like async generators. I'm not sure how TypeScript is different. You can tell it to output non-polyfilled JS with the '--target' config, which defaults to ultra-backwards-compatible ES3. Come to think of it, that might be why you're seeing both crazy generated code and slow compile times: https://www.typescriptlang.org/docs/handbook/compiler-option...