12 ms·
> In other words, I call bullshit on this entire blog post. Ignore it. Typescript is the correct solution. What is the alternative? Just using JS and letting th
by hmsimha 4y ago
> In other words, I call bullshit on this entire blog post. Ignore it. Typescript is the correct solution. What is the alternative? Just using JS and letting things break without knowing it?
It seems like you and I read a different article. The post I read did not advocate for going back to JS; rather it advocated for better tooling and documentation (with examples, ideally) for library developers
Conclusion
I love typescript and think the team working on it are incredible. Typescript has completely changed the FE landscape and wouldn't want to dismiss its contributions.
But as a library developer, we need:
- better documentation,
- better tooling, and
- to spend less time making tsc happy.
I shouldn't have to read the typescript compiler source code in order to figure out why it's resolving a piece of my code to a specific type.
I developed a library used by the React app I work on to cache network results in localstorage (to reduce the number of expensive requests made on page refreshes) and generate React-Query query-functions to read the result from localStorage first before fetching from the network as a fallback (if the data is stale or uncached).
It's not an especially large library, but I felt all of the pain points the author describes. It's great that you didn't with your library (and I'm sure there are large classes of libraries whose authors wouldn't feel these pain points), but it's a very real issue if your dealing heavily with generics, serialization/deserialization, and/or complex type interactions
- fullstackchris 4y agoDo you have an example? I've seen TypeScript handle crazy nested types (and generics) with ease.
- hmsimha 4y agoPerhaps the React-Query source itself is a good example of when "simple" is still not "easy to read/write": I picked out this file pretty arbitrarily: https://github.com/TanStack/query/blob/main/packages/react-query/src/useQueries.ts https://github.com/TanStack/query/blob/main/packages/react-q... The author of react-query seems to be very clear at communicating how the library works and the library itself provides a great developer experience, but it's also an example of how much work can go into correct typing in library code.
- mccorrinall 4y agoI recently typed my state for zustand (react library for managing state) and it was a horrible experience. An example from the docs with middleware: https://docs.pmnd.rs/zustand/typescript#middleware-that-changes-the-store-type https://docs.pmnd.rs/zustand/typescript#middleware-that-chan... If anyone calls this “easy, just learn generics” I won’t believe them further.
- cutler 4y agoHonestly, if that's what it takes to make something work with Typescript I'm surprised anyone uses it. I thought Scala/Cats was the only language/cult that indulged typism to this extent. **d save us from the static typing Spanish Inquisition and bring back dynamic languages.
- rowanG077 4y agoSeeing one the worst implementation of static typing and then blaming static typing as a whole is unfair. gradual typing is just an abomination in practice as can be seen in both TS and Python. Maybe a language that's build from the start with gradual typing in mind can make it good but I doubt it. Static and dynamic typing are just at odds in general.
- Aeolun 4y agoThe types in Typescript are amazing. I believe they’re turing complete? The problem is that this allows people to do many really complicated things that’d just be avoided or impossible in other languages. Typescript is a horrible as you make it.
- WorldMaker 4y agoYeah, this is the sentiment I keep feeling looking at most of the examples being provided in this thread and the article. A lot of the things people are doing here aren't even possible in most static typed object oriented languages or are hard to do and generally discouraged, and violate things like the Liskov Substitution Principle and Open/Closed Principle (especially the Open/Closed Principle; many of these examples are redux adjacent and a lot of them require modifying existing State object instances rather than extending them at a class level per the principle). Many of these things are easy to do in JS because JS wasn't built to be static typed object oriented language. Many of these are hard to type because the underlying language is so permissive. As library authors the desire is to be as permissive as possible, to use the "simplicity" of the untyped JS language to express an API surface that accepts any combination of possible inputs and does the greatest amount of work with that. There are many "JS native" libraries that do that, including JQuery's $ "operator" that was a swiss army knife of a million different tasks all using a single constructor which created object instances that any number of plugins mutated over time. As a JQuery user that was a very easy experience to work with, it's permissivity felt like simplicity and easy-to-learn. As someone who briefly spent time debugging JQuery types definitions in Typescript, that was an incredible nightmare. (That was also many, many versions of Typescript back with fewer typing tools, many of which would have helped a lot, but also made everything even more complex than it was at the time.) I do feel like a lot of library authors sometimes need to ask themselves as types grow more complicated in their libraries if the trade-offs are worth it. That "simple" API they are trying to give their users, is there a more "complicated" API with simpler types to use instead? Sometimes that's actually the simpler API to use understand too, but many of your end users see your types indirectly and simpler types in the API are simpler experiences of those types. The article mentions needing to add lots of overloads and that's specifically something I'm thinking about there. Another example in this thread included a function that could take a set of arguments as either an object ({ a?: thing, b?: someOtherThing, c?: thirdThing }) or tuple ([a, b, c]: [thing, someOtherThing?, thirdThing?]) and in both cases many of the parts were optional, complicated further by the thing type being generic itself and the tuple accepting different orders of parameters. Can you just pick one, object or tuple for your API? Users have "less freedom" to do as they wish and the result seems less "simple", but you can eliminate so much complexity in your types. If you do still need both ways, maybe it's reasonable to split from "overloads" to different functions setThingsObject(things: { a?: thing, b?: someOtherThing, c?: thirdThing }) versus setThingsQuickly(things: [thing?, someOtherThing?, thirdThing? ]). It's easy to armchair quarterback refactor other people's APIs, of course, but it's certainly a factor when I'm building my own APIs: These types are starting to get complicated here, should I refactor to simpler types? Should this API be split into two different/distinct endpoints to simplify the type signature? And so forth. Some complexity is unavoidable, of course, but sometimes it is worth trading "simple, permissive JS with incredibly complex types" for "tedious explosion of JS that looks complex at first glance with incredibly simple types". It's a deep trade-off space to explore and what's right for any individual project is deeply personal opinion.
- jhanschoo 4y agoIME it's precisely the expressivity of nested types and generics and variations thereof that makes it hard for the library author. The expressivity makes it possible for library authors have IntelliSense give more useful suggestions to the consumer, make automatic inference work in more places for the consumer, and make more illegal usage be impossible, at the expense of complicated type constructs. But of course all these is somewhat optional.
- drewpayment 4y agoYou answered your own question, learn how typing and generics work and you wouldn’t have had those issues. The entire article persecuted TS because the author wanted to learn quicker with little effort. Writing TypeScript in a node, app or library makes no difference. It is a language, NOT a framework.
- hmsimha 4y agoYes, and a great way to do that is to read documentation, of which Typescript is frequently lacking. For example, I got a suggestion in the discord recently to use "generic parameter defaults" for a problem I was having, which were documented... in the release notes for Typescript 2.3... and no where else: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html#generic-parameter-defaults https://www.typescriptlang.org/docs/handbook/release-notes/t...
- arinlen 4y ago> Yes, and a great way to do that is to read documentation, of which Typescript is frequently lacking. I completely disagree, and I was surprised by this sort of comment. Find me a single programming language whose docs are as good as TypeScript's docs and reference. I'd be surprised if you could come up with a single example.
- stevage 4y agoJavaScript
- rhacker 4y agoC#, C++, Java (actually I am betting no one can beat that one) I think you dismissed the actual issue he faced- gen type defaults, which I just looked is still not actually documented, it's just in the release notes. Don't get me wrong, TS is my favorite language, but let's not gloss over things.
- philliphaydon 4y ago
- paulnovacovici 4y agoWhat library is this I was looking for a good cache to store network results quite recently?
- hmsimha 4y agoIt's not open-source or really polished for consumption. I did get permission to open-source it, but unfortunately didn't have the time to improve it to the point where it would be broadly useful. In fact, now that https://tanstack.com/query/v4/docs/plugins/persistQueryClient https://tanstack.com/query/v4/docs/plugins/persistQueryClien... appears to be stable I'm thinking it might even be good to migrate to this (though it appears to require multiple queryclients if you want different cache parameters)