4 ms·
This doesn't seem like a common occurrence, judging from all the praise of TypeScript sung on this thread and elsewhere, but I've had an overall negative experi
by lewisl9029 8y ago
This doesn't seem like a common occurrence, judging from all the praise of TypeScript sung on this thread and elsewhere, but I've had an overall negative experience working with TypeScript so far.
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 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)
- crooked-v 8y agoYou can do tsc -> esnext syntax -> Babel -> whatever, though it's a pain to set up. I do agree on the extra stuff beyond types (enums, etc) being a mistake that Typescript would be better off deprecating at some point.
- 52-6F-62 8y agoMy experience is exactly the opposite of yours. 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...
- lewisl9029 8y agoI don't know if what you've described is an _opposite_ experience. I think TypeScript would work fine for me too if I never needed anything compilation-wise outside of what tsc is already capable of. But as soon as you do, the lack of extensibility of tsc compared to babel becomes painfully obvious.
- 52-6F-62 8y agoI think it is the opposite. Even without TypeScript Babel has grown to be overly complex. 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.
- lewisl9029 8y agoBabel's designed first and foremost with extensibility in mind. Of course an opinionated build tool with no extensibility to speak of (tsc), is going to be easier to set up and get started with than one that designed with extensibility as a core principle. 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-transforms 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.
- wesleyfsmith 8y agoThis is exactly my experience. If your project doesn't have a nice build pipeline for TS then it can become quite awkward and take more work, especially if you have isomorphic code that isn't all in TS already. Also, a lot of the interface syntax can get pretty weird when you are trying to type untyped libraries, which definitely slows me down. I love types in languages like java where it feels like the types have first class support--but TS by definition often involves calling out to libs that aren't written in TS which feels awkward and loses a lot of the advantages.
- lewisl9029 8y ago2. TypeScript's type checker, at least in its current state, has been downright painful to work with for functional programming with functional composition and higher order functions in general, with errors that are incredibly opaque and unhelpful, and its poor inference introduces so much seemingly avoidable type-related verbosity that it completely distorts the signal to noise ratio in our code. A prime example for this is ramda's pipe function: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/ramda/index.d.ts#L2148 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?
- lewisl9029 8y ago3. TypeScript only supports positional generic parameters (i.e. you can't give them names). 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#issuecomment-408717985 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.