A prime example for this is ramda's pipe function: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/mast...
A simple function that composes functions together in reverse order requires:
1) The definition to be an exhaustive list of all the variations of input functions that pipe can accept (which of course would technically require an infinite number of declarations to fully specify, so ramda had to call it quits at 10).
2) The user of pipe to specify output types for every single function being composed at every step, even though that should be possible to infer using the return type of each function in the pipeline.
These kinds of limitations plague our codebase everywhere we try to define higher order functions in our own code as well, because it means we have to ask users of the function to specify the result of the function argument as a generic parameter even though it should be perfectly inferrable from the function argument itself, and the noise buildup becomes exponential as you start composing higher order functions together due to pipe suffering the same limitation. The issue also rears its head when using higher order components with recompose, which we make heavy use of as well.
I'd love to hear how other teams working with functional programming in TypeScript work with these limitations. Perhaps we're missing something fundamental that could vastly improve our experience?
It's well known that positional semantics don't scale when it comes to argument lists, because when changing the API of something with a positional argument list, adding an argument in an arbitrary position becomes a breaking change that requires all usages to be updated.
Unfortunately, part of the fallout from point 2 means we often need to define higher order functions that require a decent number of generic parameters, all of which have to be positional due to this limitation.
There's a hack that we discovered that allows you to have poor-man's named generics by using a single generic argument with named properties, inspired by this comment: https://github.com/Microsoft/TypeScript/pull/23696#issuecomm...
But it's incredibly verbose, and doesn't support optional generic arguments or defaults as far as I can tell. We still use it despite this, because we believe the verbosity and having to specify every generic argument is the lesser of two evils compared to offering APIs that requires breaking changes for every addition. But that we have to make this choice points to a glaring omission in the design of the type system.
Babel has always caused me headaches. Setting up Babel-based build systems is troubling—the same setup will work sometimes and not others even on the same machine. TypeScript vastly simplified working with modern JS for me. It’s consistent, requires far fewer dependencies, and is much more succinct.
YMMV, of course...
If you’re talking about front end libraries like React you’re better off ditching Babel altogether and going with something like Parcel. It just works.
The TS developer experience ya sim proved working with JS enormously for me when I have to do it, and nudged me toward simpler, smarter development environment setups.
I can’t speak for you or anyone else. Just adding my two cents.
That doesn't mean one's necessarily fundamentally better or worse than the other, but the great thing about extensible tools is that they can often be used as building blocks for other more opinionated tools that abstracts away configuration details from the user by providing an opinionated set of defaults, and still offer the ability to customize when users' needs are not adequately met by the defaults.
Case in point - Parcel actually uses babel under the hood:
https://parceljs.org/javascript.html#default-babel-transform...
All it does is configures a default set of plugins & presets for you. You can override any and all compilation behavior Parcel provides by default simply by adding the appropriate babel config files.
TypeScript could have done the same with their build tooling, by extending babel with plugins/presets and providing the exact same set of functionality to users as what tsc's is capable of currently in terms of compilation features (which is currently afaik a strict subset of what babel + its plugin ecosystem is able to provide).
That would give users the exact same out-of-the-box experience as tsc does currently, and offer them the opportunity to extend compilation behavior with additional plugins and presets when the need arises.
That extending the compilation behavior of tsc, after so many years of development and wide adoption, is still not even on the roadmap right now is the core of my disappointment with them choosing to develop their own _non-extensible_ compilation tooling vs just extending babel, where they would have been able to offer extensibility for free from day 1.
I do agree on the extra stuff beyond types (enums, etc) being a mistake that Typescript would be better off deprecating at some point.
Here's some thoughts on what has contributed to that so far:
1. TypeScript pushes you towards its own build pipeline (based on tsc) that doesn't play nicely all the time with mainstream JS build pipelines (usually based on babel).
With babel 7 came @babel/preset-typescript, which I hoped would narrow the gap, but so far it's very clearly a second class citizen in the TypeScript ecosystem, with new features built only with tsc-based pipelines in mind (Project References is the one that stands out because we sorely need it for our monorepo codebase, but can't use because we chose to adopt @babel/preset-typescript), and having personally ran into several issues stemming from what seems to be fundamental incompatibilities between tsc and babel that have no real workarounds (here's one that I can remember off the top of my head: https://github.com/babel/babel/issues/8361).
The reason this is so frustrating is TypeScript could have been just a type checker, like Flow. But instead, it had to introduce it's own compilation tooling which is still vastly inferior to babel in terms of overall flexibility and extensibility. All of this seems to be due to what I believe are a few fundamentally poorly thought-out decisions at the beginning of the project to allow TypeScript to specify its own language features that have runtime semantics (things like Enums and class visibility modifiers, neither of which are anywhere close to becoming standardized in JS-proper, by the way, and both of which are completely orthogonal to TypeScript's main responsibility of type checking), and implementing them using a separate build pipeline instead of as extensions to babel & its own type checker.
If I were to use a JS type system for a new project today, I'd personally choose Flow over TypeScript in a heartbeat because of this.
(this is getting a bit long so will continue in a reply)