[HN Gopher] TypeScript is terrible for library developers
       ___________________________________________________________________
        
       TypeScript is terrible for library developers
        
       Author : qudat
       Score  : 177 points
       Date   : 2022-08-23 18:31 UTC (4 hours ago)
        
 (HTM) web link (erock.prose.sh)
 (TXT) w3m dump (erock.prose.sh)
        
       | finder83 wrote:
       | Like many here, I love typescript for library writing, at least
       | internally. I've not made any big npm packages, so not sure how
       | that would do, but I imagine I'd like it a lot too.
       | 
       | What I hate is getting the build system set up to generate the
       | right combination of .js/.d.ts files to work with the build
       | system of whatever I'm using it in, particularly for libraries
       | that use any form of preact/tsx files. I must be seriously
       | missing something, but after 5 years as a node/react developer I
       | still suck at making build pipelines for new projects.
        
       | franciscop wrote:
       | The only convincing reason to use TS I've seen so far as a
       | library author, which TBF is actually not about TS but about
       | TSDoc/JSDoc, is how the autocomplete in VS Code works. Going from
       | an empty/barebones autocomplete to actually having the
       | documentation in-line is a pretty nice extra feature for my users
       | IMHO:
       | 
       | https://twitter.com/FPresencia/status/1545932895126175746
        
       | brundolf wrote:
       | I find that most of the pain/complexity around TypeScript types
       | happens when you're trying to write super dynamic or polymorphic
       | JavaScript-style code and then put types on top. This comes out
       | especially when adding types to an existing codebase. I can
       | definitely see how this would affect library authors more than
       | application authors, but I'm not sure it's TypeScript's fault,
       | and I think it can be avoided by making different choices about
       | what interfaces you expose
       | 
       | Or, worst-case, just weakening your types. Plenty of libraries
       | have weaker types than they could have, but sometimes it's
       | justified because it would be hard to get them exactly right
        
       | Hirrolot wrote:
       | I have a similar essay entitled "Why Static Languages Suffer From
       | Complexity" [1]. I don't deal with TypeScript, so it's up to you
       | whether the essay relates to it or not. Basically, the idea is
       | that languages make type-level and value-level programming
       | different in surface, while they're (or at least can be) the
       | same. From hence we have type-level astronautics that boggles
       | library developers' minds.
       | 
       | [1] https://hirrolot.github.io/posts/why-static-languages-
       | suffer...
        
       | nwsm wrote:
       | The article boils down to asking for the TS team for better
       | documentation and tooling. Hard to argue with that I guess.
       | 
       | > In effect, we are shifting complexity from end-developers to
       | library developers.
       | 
       | Is this not why libraries are written in the first place?
        
       | vosper wrote:
       | Something I've long wanted is a type-explainer - something that
       | will let me highlight parts of a type definition and tell me in
       | English what the fragment means. The example that OP linked
       | elsewhere in this thread [0] is a good one - I know what parts of
       | that mean, but I've never seen "T extends string = string"
       | before.
       | 
       | [0] https://news.ycombinator.com/item?id=32569708
        
         | Smaug123 wrote:
         | It's just "T extends string", with a default value of `string`
         | if you don't supply T.
        
           | [deleted]
        
         | throwaway247322 wrote:
         | That would be cool to see, actually reminds me of the cdecl
         | explain project [1].
         | 
         | [1] https://cdecl.org/
        
       | kansface wrote:
       | | How do library developers debug their highly dynamic and heavy
       | use of conditional types, overloads?
       | 
       | Don't write that code because it's impossible to debug and test?
       | If typescript yields pain here, it's appropriately felt imo.
        
       | zethraeus wrote:
       | If you write your library in typescript, not JS, do you still
       | have any of these problems?
       | 
       | If not, it sounds like one core issue is 'adding types to an
       | existing untyped codebase is hard'. This checks out. :)
        
         | no_way wrote:
         | Writing new library and expecting people to actually use it
         | without providing types is not realistic. You can write library
         | in JS, but you will still need to provide types, since that's
         | what ecosystem is right now.
        
         | wizofaus wrote:
         | I disagree it's hard per se, but I'd agree it's hard to do
         | well. Especially if you have a bunch of JS functions that
         | return (or expect) objects with very similar but slightly
         | different properties. Figuring out the overlap and the semantic
         | relationship between the types is often challenging, because
         | the original authors never thought about the problem in terms
         | of what types of objects they were dealing with.
        
       | lucideer wrote:
       | Typescript is bad for library developers who have produced bad[0]
       | APIs and don't want[1] to refactor them.
       | 
       | Overloads exist in Typescript because as a language it needs to
       | support what javascript supports - regardless of whether the
       | pattern is "good". Overloads are heavily used in DefinitelyTyped
       | because contributors there haven't had the luxury of designing
       | the APIs they're typing. If you're designing your own API, you
       | shouldn't be heavily using overloads because typed languages
       | don't encourage the ridiculous trend of movable arguments that
       | jQuery pioneered in the JS world.
       | 
       | Similarly, if you're writing all of your own code you shouldn't
       | need type manipulation because you shouldn't be leaning heavily
       | on variable mutation. Dogmatic stances on immutability are
       | reasonably a matter of debate, but it's fairly uncontroversial to
       | state that minimising mutation in your data flows helps prevent
       | your code becoming an unmaintainable mess.
       | 
       | Etc.
       | 
       | [0]Writing good APIs is hard - bad APIs are inevitable. Typed
       | languages make refactoring faster.
       | 
       | [1] Refactoring untyped languages is very hard and the tendency
       | not to is deeply ingrained and very natural/reasonable.
        
         | ajkjk wrote:
         | This, 100%. In this very comment section there are a bunch of
         | people saying "I maintain <API with crazy unnecessary
         | complexity in it> and I can't figure out how to translate it to
         | TS". Well yeah, that API sucks, make a simple one instead.
        
       | dangarbri3 wrote:
       | I think valid points are bad about typescript being bad, like
       | without a real debugger it's going to be hard to debug anything.
       | 
       | But I firmly disagree with how the article begins.
       | 
       | > In effect, we are shifting complexity from end-developers to
       | library developers.
       | 
       | That is the whole point in creating a library. To hide complexity
       | in a nice easy to use interface/API.
        
         | johnfn wrote:
         | > That is the whole point in creating a library. To hide
         | complexity in a nice easy to use interface/API.
         | 
         | Totally agree. Minor ergonomic changes in a library have a
         | wildly disproportionate impact on developers. If I save a
         | developer 5 seconds with a better type, and I have 1000
         | developers using my library, that's 5,000 seconds I just saved
         | - and that's assuming they only ever use my type a single time!
         | With this in mind, I'm totally OK paying a tax on making
         | libraries a bit harder to write, if it means that the benefits
         | of stronger types fan out. After all, writing that type could
         | take 4,995 seconds for me to write and still be a net positive.
         | 
         | And honestly, 5 seconds isn't even close to accurate. Good type
         | definitions have saved me hours.
        
         | Smaug123 wrote:
         | Yeah, the author appears to be complaining that Typescript is
         | forcing them to stay honest even while they perform Mad
         | Shenanigans like dynamically constructing types :/ if TS didn't
         | check it, it would be down to your users to report the bugs in
         | production!
        
           | franciscop wrote:
           | I don't think it's very fair. As a library developer myself,
           | you try to make you user lives easier, which implies being
           | flexible in what you accept when possible. A couple of
           | examples I struggled with recently:
           | 
           | Documenting https://umbrellajs.com/documentation#addclass.
           | The way I documented it is by opening with a code snippet
           | with many of the possible options (which can also be
           | combined!):                   .addClass('name1')
           | .addClass('name1 name2 nameN')
           | .addClass('name1,name2,nameN')         .addClass('name1',
           | 'name2', 'nameN')         .addClass(['name1', 'name2',
           | 'nameN'])         .addClass(['name1', 'name2'], ['name3'],
           | ['nameN'])         .addClass(function(node, i){ return
           | 'name1'; })         .addClass(function(){ return 'name1'; },
           | function(){ return 'name2'; })
           | 
           | This is pretty easy to do in plain JS, and of course if you
           | are writing code and using it you just read the first 1-4
           | lines and know what to do for 99% of the cases, while also
           | noticing there's few "advanced/flexible" ways of using it.
           | How would you even do that in TS?
           | 
           | Then there's a classic initializer in JS that works like
           | this:                   function myLibrary(arg) {
           | if (!(this instanceof myLibrary)) {             return new
           | myLibrary(arg);           }           ...         }
           | 
           | This is very useful to create a library like jquery that you
           | can initialize straight away without needing (but also being
           | able to use) the `new` keyword, just calling it like a
           | function and always ensures it returns an instance. To this
           | day I haven't found a way of doing this in TS.
        
             | josephg wrote:
             | You can list variants of a function's signature in
             | typescript, but typescript won't help you much with
             | "stringly typed" things (like
             | `.addClass('name1,name2,nameN')`).
             | 
             | Different languages have a grain like wood does. And that
             | subtly directs you by making some things ergonomic and some
             | things difficult to express. I love typescript, but I
             | definitely find it changes the resulting code.
             | 
             | Typescript makes "jQuery style" javascript much more
             | awkward to write, because its harder to type. This is good
             | and bad. I write less scrappy code in typescript - which I
             | think makes it a worse language for quick prototyping. But
             | the tradeoff is that I think its a better language for
             | larger teams / longer lasting projects where functions are
             | read a lot more than they're written.
             | 
             | The actual typescript answer for your API is "don't make
             | your API look like that". Its not always the answer you're
             | looking for.
        
             | overgard wrote:
             | Honestly, that's just silly. There's absolutely no reason
             | to accept that many different call styles. Why not just
             | take in an array? As a user I don't find things like this
             | convenient, I find them to be confusing footguns. It's a
             | one liner to split your comma separated string into an
             | array as an end-user, but once you add that complexity to
             | the interface in the library you can never ever take it
             | out.
        
               | LtWorf wrote:
               | I thought in the js world it was normal to break
               | compatibility whenever.
        
             | LelouBil wrote:
             | A lot of times making the user's life easier is about
             | having a single correct way to do something.
        
             | Smaug123 wrote:
             | There's "be flexible in what you accept", and then
             | there's... "take a comma-separated string which you could
             | parse into your arguments" :)
        
             | robbiejs wrote:
             | I agree. I am the creator of the data table lib
             | datagridxl.com and I like to make my methods as flexible as
             | possible. Example:
             | 
             | grid.selectRows(2) // index grid.selectRows([3,5]) // range
             | grid.selectRows([[1,2],[4,6]]) // multiple ranges
             | 
             | It fits in the JavaScript spirit of "we will make it work"
             | which I love.
             | 
             | Other major thing that made me decide to develop in es6
             | instead of typescript was compilation times. After a ctrl+s
             | it had to compile ts to js for 10 seconds, which is
             | annoying for me, as i like to check & test every minor code
             | change.
        
               | int_19h wrote:
               | > It fits in the JavaScript spirit of "we will make it
               | work" which I love.
               | 
               | Do you also use == and != for comparisons by default?
        
             | HideousKojima wrote:
             | >To this day I haven't found a way of doing this in TS.
             | 
             | Just make a static method that does initialization if
             | needed and returns a new instance?
             | 
             | https://www.typescripttutorial.net/typescript-
             | tutorial/types...
             | 
             | I'm trying to not be too hard on people as I read this
             | thread, but it's baffling to me that web devs are getting
             | filtered by features that have been in other languages
             | since the 80's and 90's.
        
             | phailhaus wrote:
             | declare function addClass(...classes: (string | string[] |
             | (() => string))[]): void;
             | 
             | It's pretty straightforward in Typescript. And when you go
             | to implement it, tsc will make sure you cover all the types
             | your function claims to support.
             | 
             | > This is very useful to create a library like jquery that
             | you can initialize straight away without needing (but also
             | being able to use) the `new` keyword, just calling it like
             | a function and always ensures it returns an instance.
             | 
             | Avoiding having to type "new" is not a very compelling
             | reason to avoid Typescript, especially because Typescript
             | won't let you make the mistake of calling the function
             | without it. It's just not a problem.
        
               | franciscop wrote:
               | That's an order of magnitude less clear in what the
               | function expects than the examples I gave IMHO
        
               | phailhaus wrote:
               | Who's stopping you from giving examples in a docstring?
        
               | [deleted]
        
             | darepublic wrote:
             | the type definition of the same is clearer imo and not only
             | that it is enforced by both the ide and the compiler.
             | Libraries typically DO NOT write a bunch of code examples
             | of all the legitimate arguments that can be passed. Also in
             | your example above, > .addClass(function(node, i) {return
             | 'name1'}) ^ what is node? What is i? Seems intuitive to
             | think it must be a dom reference and an index. But in
             | different domains it's not always gonna be so clear. Like I
             | am not familiar with umbrella js, but maybe node could be a
             | jquery object and not a plain dom ref? With typescript you
             | can just say
             | 
             | type GetClassName = (node: HTMLElement, i: index) => string
             | | string[] // see how I added string[]. So I don't have to
             | add yet another example // of a function returning a string
             | array instead of a string
             | 
             | and then add it to the union of types that can be passed
             | into addClass. Great, no more guessing based on the domain
             | knowledge I have (or don't), it's crystal clear. And it
             | forces the lib developer to have the discipline to make it
             | crystal clear, which they usually don't I'm afraid.
        
             | bzxcvbn wrote:
             | Your problem would be solved pretty easily by making two
             | changes to your API:
             | 
             | * Only accept an array of strings. Not a single string, not
             | several strings, not several arrays of strings, and
             | certainly not a space/comma-separated list of classes.
             | 
             | * Add another single overload where you accept a function
             | that takes two parameters. That function can ignore its
             | parameters if it wants to, and it returns a list of
             | strings, so you don't need to accept several.
             | 
             | You have the same functionality, it's not harder to use for
             | an end-user, and it's infinitely simpler to type.
        
             | wvenable wrote:
             | Yeah I kind of disagree that "being flexible in what you
             | accept when possible" is a benefit to the user. It's just
             | more complexity pushed down to the user that is
             | unnecessary.
             | 
             | In this example, I'm not sure why it's the functions
             | responsibility to support all of these options when the
             | user is perfectly capable of manipulating strings and
             | arrays.
        
               | jbm wrote:
               | While I agree with you, I have seen practical cases where
               | sensing an array of items in the get parameter of a web
               | server is handled differently, similarly to what the
               | parent comment mentioned.
        
             | tengbretson wrote:
             | If TypeScript steers authors away from either of these
             | patterns then all the better in my opinion.
        
         | qudat wrote:
         | Thanks for reading the article!
         | 
         | > That is the whole point in creating a library. To hide
         | complexity in a nice easy to use interface/API.
         | 
         | I think that's a fair point, but generating types that satisfy
         | all use cases is very challenging to get right --
         | disproportionately so. I could see a world where -- without
         | proper tooling and growing complexity -- typescript libraries
         | becomes so difficult to maintain that people give up or burn
         | out. Maybe that's a pessimistic outlook but I already feel that
         | way some days.
        
           | Chabsff wrote:
           | In strongly-typed programming languages, which includes
           | Typescript, figuring out the types *of the interface* is not
           | something that's done after the fact. `@types/*` is an
           | exceptional project meant to back-port JS libraries to
           | TypeScript, but that's the exception, not the rule.
           | 
           | If you write a library in TypeScript, determining what types
           | are present as part of the interface is one of the very first
           | thing that should be done.
        
             | mrkeen wrote:
             | > In strongly-typed programming languages, which includes
             | Typescript, figuring out the types is not something that's
             | done after the fact.
             | 
             | Too broad a statement. There's loads of value in having a
             | compiler figure out types for you after/when you write the
             | code.
        
               | Chabsff wrote:
               | Hard disagree as far as the portions of it that are part
               | of the user-facing public interface are concerned.
               | 
               | But granted, as a general rule you are correct. I was
               | referring specifically to API interfaces.
        
               | overgard wrote:
               | If you're talking about type-inference, sure, I guess
               | it's fine.
               | 
               | If you're talking about figuring out what types you're
               | going to accept, you should absolutely be defining that
               | on your own up-front. If you don't even know what your
               | types are how is an end user going to figure it out?
        
         | heisenbit wrote:
         | Often libraries are bootstrapped from application code. Having
         | a step function in complexity is not helpful in fostering an
         | ecosystem with a broad range of maturity.
        
       | nichochar wrote:
       | >In effect, we are shifting complexity from end-developers to
       | library developers. This places a huge burden on us to be experts
       | with how typescript works.
       | 
       | Interesting, I had never thought of this tension, but I think I
       | disagree with the author and think this is a good tradeoff. We
       | shouldn't assume all developers are library developers, most
       | beginner developers for example don't write libraries or
       | shouldn't. I think it's actually good to force competence in the
       | library author domain (within reason), since they can abstract
       | away this pesky complexity (the point of any good library).
       | 
       | I like this new mental model, but I still agree with the author
       | that typescript is too hard to work with and developer experience
       | with it has a ways to go.
        
       | acemarke wrote:
       | Heh, it's amusing to see Redux Toolkit referenced here. I'm one
       | of the two main RTK maintainers. My co-maintainer Lenz Weber is
       | responsible for most of our TS type wizardry.
       | 
       | Agreed that writing TS types for libs can be a pain. I actually
       | did a talk recently on "Lessons Learned Maintaining TS Libraries"
       | [0], where I talked about some of the techniques we used, and
       | some possible TS changes that would be helpful for us as
       | maintainers.
       | 
       | As one recent example, TS made a change in a 4.8 pre-alpha that
       | broke RTK's `createSlice` types. Lenz tried to come up with a
       | fix, couldn't, and had to add a workaround to check what TS
       | version is being used and specifically use an altered type. Since
       | there _isn't_ a good way to know what TS version is being used,
       | Lenz resorted to hacking together a new package that abuses the
       | `typesVersions` property to define a different TS type for
       | _every_ TS major+minor version combo, and then used that to
       | decide what the RTK type should look like conditionally [1].
       | 
       | Another pain point is debugging type transformations. I reworked
       | the Reselect types in 4.1.x to do a much better job of inferring
       | the argument types for the final selector, based on the
       | intersection of all the input selector arguments. This ended up
       | as a monstrous type that does a types-level map + transpose +
       | intersection [2]. It took me weeks to get this working right, and
       | I frequently had to break it down into multiple small
       | intermediate types to see how TS was processing each step.
       | 
       | I know that someone on Twitter was recently working on an
       | alternate TS type-checker based on bytecode, and they said they
       | had some kind of a working types-level debugger [3]. Having
       | something like that officially, where I could see each step of
       | how TS was transforming the types, would be _hugely_ valuable.
       | 
       | There's a couple folks like AndaristRake who are able to dig into
       | the internals of the TS compiler itself to trace how it's
       | interpreting the types. I definitely don't have that ability :)
       | 
       | [0] https://blog.isquaredsoftware.com/2022/05/presentations-
       | ts-l...
       | 
       | [1] https://github.com/reduxjs/redux-toolkit/pull/2547
       | 
       | [2]
       | https://github.com/reduxjs/reselect/blob/v4.1.5/src/types.ts...
       | 
       | [3] https://twitter.com/MarcJSchmidt/status/1539787500788613120
        
         | helsontaveras18 wrote:
         | I agree wholeheartedly that debugging types is very difficult.
         | The best we have is hovering our mouse over the definition to
         | see what type was created from the definition. Obviously not
         | great.
         | 
         | Also, debugging type differences with deeply nested objects
         | (like what happens with graphql schemas) can be hugely painful.
         | You need to copy the error message to its own file (since the
         | errors can be huge) and debug what specific piece failed.
         | 
         | I do feel the Typescript documentation is lacking and the only
         | way to get better is to read open source projects to see how
         | others have done it... which is only helpful when they've
         | solved the exact problem you're looking for.
         | 
         | It'd be great to have a recipe book of common advanced
         | typescript manipulations and how to compose them together.
        
       | azangru wrote:
       | Terrible as opposed/compared to what?
       | 
       | Maybe a language with better type guarantees would be better
       | suited for library developers; but if the alternative is plain
       | javascript, then no. Just no.
        
       | darepublic wrote:
       | > Types add a lot of code to your library. When first attempting
       | to contribute to a project, one must grok the application logic
       | as well as the type logic.
       | 
       | To me types are very welcome. It's much easier to grok a function
       | when I can see a type interface for it. Some libs do this better
       | than others. Some libs have types as an after thought and you get
       | confronted with something like:
       | 
       | ``` export type Parameters = {[key: string]: any} ```
       | 
       | which is basically a giant FU and is as helpful as no type. But
       | to me, well maintained type api is a great boon to both
       | contributors and end developers.
        
       | andrewallbright wrote:
       | I think the author is good with naming symptoms and I think they
       | miss the true culprit. It's not typescript that is causing this
       | pain, no it's the pain of getting correct abstractions laid down
       | in code. It's truly the pain of code design activity. Typescript
       | and library programming is just forcing that pain to the surface
       | instead of hiding it away.
       | 
       | I know this because I struggle against these "pain during code
       | design" & "get abstractions right" forces when I program.
       | 
       | Declaring types is really declaring what domain objects your
       | program cares about and function signatures are really just
       | describing interactions that can happen. So what 'domain objects'
       | should meaningfully exist in my program and how do they interact?
       | A bad abstraction can burn up a lot of time, so make sure you
       | check your abstraction. I typically check mine describing stories
       | to my friends (or rubber duck) and ensuring I'm using plain
       | English and the other person doesn't get lost. YMMV
       | 
       | When you're doing web client programming you're typically using a
       | library to accomplish work, and much of that work is reacting to
       | user input and rendering data. I would argue that because so much
       | of the linguistic heavy lifting has been done for web client
       | programming, practicing the abstraction exercise is done less
       | than in other programming domains. Doesn't mean it couldn't, just
       | trying to highlight a difference in programmer domains. This is
       | why the pain is more apparent in library programming than web
       | client programming.
       | 
       | I imagine that advanced individuals in code design understand
       | more of the connection between math and programming and are
       | capable of describing systems of interesting work in few simple
       | statements. I hope to reach those heights someday.
        
       | chadlavi wrote:
       | As someone who started our inner-source react component lib with
       | typescript, it's actually really great
        
       | ummonk wrote:
       | > There are a lot of reasons why typescript sucks for library
       | developers, but at the end of the day it reduces developer
       | productivity. In effect, we are shifting complexity from end-
       | developers to library developers.
       | 
       | Isn't that the whole point of libraries? Instead of having a
       | hundred end-users trying to understand a library's untyped API,
       | we save overall developer time by having just the one library
       | developer type its API.
        
       | 734129837261 wrote:
       | TypeScript has come a long way, but one of its biggest issues is
       | the complete lack of FORCED sensible naming conventions.
       | 
       | Generics are the worst offender, with people often using a single
       | letter to represent something that's both hard to keep track of
       | and impossible to intuitively make sense of.
       | 
       | Any type should be: `TUser` and not `User`.
       | 
       | Any generic should be: `<GPerson>` and not `<P>`.
       | 
       | Any interface should simply not exist because it's TypeScript,
       | not InterfaceScript. The addition of classes into JavaScript was
       | unnecessary sugar and they should just do away with it. So,
       | `type` for everything, no more `interface`.
       | 
       | But that's a whole different subject.
       | 
       | I've also noticed that people over-engineer TypeScript. I had a
       | team mate telling me he wanted to make URLs type-safe. I asked:
       | "But why?" and he had no answer. There was no problem preceding
       | it. There wasn't anyone asking for it. It wouldn't solve
       | something we didn't know. It would just make the codebase more
       | confusing to deal with because, in his solution, there wasn't a
       | single place where routes were defined. Nope, routes would be
       | inferred from Pages and their use of Page props.
       | 
       | It was genius. But it also was over 2000 (two THOUSAND) lines of
       | code that I didn't feel like I wanted to deal with.
       | 
       | I gave him permission to publish it and get the open-source
       | community involved. Once it's tried & tested over the course of
       | many users and lots of time, we could consider it.
       | 
       | He wasn't happy. He was also not very pragmatic. TypeScript seems
       | to do that to people. The "never any `any`"-crowd is so tiresome.
        
         | erulabs wrote:
         | > It was genius. But it also was [...] code that I didn't feel
         | like I wanted to deal with
         | 
         | You sound like a sysadmin my friend! This is exactly how I feel
         | about _plenty_ of software that I 've allowed into production,
         | but that now keeps me up at night and distracts me from
         | spending time with my kids.
         | 
         | One of the most useful things about plain JavaScript at this
         | point is seeing if it causes genuine disgust. It's a razor for
         | idealism versus pragmatism.
        
         | schwartzworld wrote:
         | > The addition of classes into JavaScript was unnecessary sugar
         | and they should just do away with it.
         | 
         | Weird. Classes are one of my favorite features, even before
         | using TS. I do a lot of data modeling using functional
         | techniques and the class keyword allows a really intuitive way
         | of encapsulating things.
        
       | yashap wrote:
       | Another way to phrase this is "statically typed languages suck if
       | you want to write highly dynamically typed code." That's true. I
       | think there's 3 options here:
       | 
       | 1. Keep writing code in a very dynamically typed style, despite
       | choosing a statically typed language. Just deal with/stomach the
       | extreme type complexity that is necessary to model your dynamic
       | style statically
       | 
       | 2. Keep writing code in a very dynamically typed style, and
       | switch to a dynamically typed language
       | 
       | 3. Stop writing code in a very dynamically typed style. If you're
       | using a statically typed language, write code that embraces
       | static types
       | 
       | IMO 2 and 3 are both reasonable choices, but the author is
       | deciding to choose 1, which is of course painful. I strongly
       | disagree that libraries must be super dynamic though - that's a
       | pure style choice that some library authors adopt, but you can
       | absolutely write basically any library in a static types friendly
       | style, you just need to reflect that in your interfaces.
       | 
       | I guess the one thing that _IS_ TS specific is that ppl can write
       | their libs in JS, then try to add types to it, and if they chose
       | crazy dynamic interfaces, it will be an incredible pain to
       | statically type. But honestly, you could just say "this is a JS
       | lib, there will be no TS types."
        
       | [deleted]
        
       | cellis wrote:
       | I just wrote and published a library and CLI (for migrating
       | postgres databases) to NPM using Typescript (
       | https://npmjs.com/package/nomadic ).
       | 
       | The Typescript itself, coupled with jest TDD made development
       | very pleasant. The hardest part for me was figuring out how to
       | publish the types so when a user installs it using yarn install,
       | they get Intellisense. I think I did a poor job of it, as in the
       | end I just published the source code. Even though I prefer when
       | npm libraries do this ( as I can inspect the source in my editor
       | easily ), I know the pros don't do that. If anyone wants to take
       | a look and knows how to solve this type publishing, I'd be very
       | appreciative.
        
         | acemarke wrote:
         | Glancing at it very quickly via Unpkg, I _think_ you really
         | just need to fix the broken `types` path reference in
         | `package.json`.
         | 
         | Right now, your `dist` folder has `dist/index.d.ts` available,
         | but `package.json` points to:                   "types":
         | "./lib/index.ts"
         | 
         | Just change that to:                   "types":
         | "./dist/index.d.ts"
        
       | Veuxdo wrote:
       | "testing your types"
       | 
       | If you find yourself thinking you need to test your types, you've
       | taken a wrong turn somewhere.
        
         | Hirrolot wrote:
         | Does it mean that languages should prohibit type-level
         | programming (or type-valued functions), like in Idris?
        
           | Smaug123 wrote:
           | In Idris, you've _typed_ your types. Much easier to deal
           | with.
        
       | tannerlinsley wrote:
       | TLDR: I write and maintain several good-sized typescript
       | libraries so I write library types all the time. In fact, I'm
       | writing them right now! TypeScript is what makes them really fun
       | to use. They infer everything they can and have very high levels
       | of safety and passthrough while still allowing for composition
       | and extension at the framework-adapter and library level. I will
       | never consider writing another OSS tool in the JS ecosystem
       | without ensuring the typescript experience is the best I can
       | offer.
       | 
       | That said...
       | 
       | I've learned that "library" types might not be the best way to
       | talk about these concepts. What we're really talking about here
       | are types that are complex/advanced enough to force you to
       | venture beyond the primitive building blocs in the TS docs and
       | into existing open source solutions that have trail-blazed more
       | advanced use-cases. It took me only a few weeks to get
       | comfortable with common TS, but at least a year to feel dangerous
       | enough to write more advanced TS. I'm on year 3 now and I am
       | still learning/forgetting so much about it.
       | 
       | I agree that: - Advanced types and their concepts are difficult
       | to learn. - There is limited documentation on how to create/use
       | them. - They can sometimes be difficult to reason about, mainly
       | due to the limited syntax TS offers around advanced concepts.
       | 
       | On the the positive: - They can be learned with practice. - There
       | is plenty of OSS out there to learn from - Once you learn them,
       | you start to think differently about TS as a language instead of
       | annotations
       | 
       | I wish there were better features/syntax support for: - Optional
       | generics - Higher-order generics / Polymorphic generics
       | (basically higher-order functions, but for types) - More built-
       | ins (like ts-toolbelt, type-fest, etc)
       | 
       | I think this ramp of difficulty with advanced TS types is fine
       | for the most part. Library authors have always carried way more
       | of a burden than devs at the edge, even during runtime to ensure
       | things like size, performance, flexibility, etc. TS is just
       | another facet that is becoming more an expectation every day.
       | 
       | At the end of the day, a library dev gets to choose what level of
       | investment they'll put into great types for their library. If
       | they can pull it off, their tool will likely provide a measurably
       | better developer experience.
       | 
       | That's my goal for my libraries, so choosing to go all-in on
       | advanced TS is now a no-brainer.
       | 
       | Anywho, good luck!
        
       | jbirer wrote:
       | (Hoping to not invalidate anyone's concerns with this comment)
       | 
       | The majority of complaints I heard on Typescript were in the
       | lines of: "I should be able to do this X thing that would not fly
       | in a static language / not a good idea". Many times Typescript
       | caught me doing stupid things like passing the wrong type or not
       | checking data adequately, also forcing me to comment my code by
       | describing shape of the data. It was a life saver for debugging,
       | though it's not perfect, it's more like lipstick on a pig for a
       | badly designed language.
        
       | harrisonjackson wrote:
       | I don't depend on the actual typescript docs much but thankfully
       | in @types and in tons of repos there are examples of well written
       | typescript code.
       | 
       | The amount of JS and TS out there is also a bit of a foot gun
       | though so stick with heavily used/starred libs if you aren't
       | sure.
       | 
       | One tool that helps a lot with developing libraries in typescript
       | is TSDX[0] or its successor dts-cli[1] and there is a bunch of
       | good stuff in awesesome-typescript[2].
       | 
       | Maybe library devving is harder?(more work?) with tyepscript but
       | it is worth it for the end developer, especially if that end
       | developer is you. If you aren't using your own libs then you're
       | probably getting paid by someone else to make them or... idk.
       | 
       | [0] https://github.com/jaredpalmer/tsdx
       | 
       | [1] https://github.com/weiran-zsd/dts-cli
       | 
       | [2] https://github.com/dzharii/awesome-typescript#libraries
        
       | lucideer wrote:
       | > _Why are there no guides on the typescript site about library
       | developers? What about a guide on the recommended tools for a
       | library developer?_
       | 
       | I have no idea what this means. What are "tools for a library
       | developer"?
       | 
       | > _I think the underlying assumption here is that there is no
       | difference between a library developer and an end-developer,
       | which I reject._
       | 
       | Expansion on why they reject this would be of interest here. I'm
       | a library developer - I'm not aware of any differences.
        
       | yesimahuman wrote:
       | This hasn't been my experience, certainly not in 2022 with all
       | the progress typescript tooling has made. Testing _is_ annoying
       | to set up the first time but once you have it working you barely
       | ever have to mess with it.
       | 
       | As a library author myself I struggled a lot more with monorepo
       | management and changelogs/publishing but tools like
       | turbo/nx/changesets have really helped here.
        
       | paulddraper wrote:
       | > Why are there no guides on the typescript site about library
       | developers? What about a guide on the recommended tools for a
       | library developer?
       | 
       | One interesting thing about Typescript is that it prefers .ts to
       | .d.ts files.
       | 
       | So if you ship you .js, .js.map, .ts, and .d.ts files next to
       | each other, your downstream consumers will be running expensive
       | type inference on the .ts.
       | 
       | Instead, you have to manipulate package.json entries so that the
       | .ts and the .d.ts are separate. (Or inline sources into source
       | maps and exclude .ts from the package.)
        
       | rowanG077 wrote:
       | I don't have written much TS but the author's experience is
       | exactly what I felt while writing TS. The bolted on "gradual"
       | type system makes typing harden then even dependently typed
       | languages I have used. I honestly believe TS has done
       | irrecoverable damage to the perception of types to millions of
       | web developers.
        
       | throwaway247322 wrote:
       | > testing types
       | 
       | Right, this happens in any language where you are writing type
       | lambdas or functions at type level, which makes sense actually.
       | We've been doing this in C++ as well for years...
       | 
       | On another note, Redux is fundamentally a bad experience in
       | typescript because the action type must extend from `AnyAction`,
       | so it poisons the well.
       | 
       | What we need is a first-class typescript state container with a
       | rigid API, even better would be first-class enums and pattern
       | matching...
       | 
       | Writing generators isn't fun in typescript, so yes I imagine
       | redux-saga would be painful.
        
         | acemarke wrote:
         | Note that our official Redux Toolkit package, linked in the
         | article, has complex TS types specifically _because_ we work to
         | simplify the TS usage experience for our users.
         | 
         | Here's all you need to do as a typical Redux app developer to
         | get started using Redux Toolkit with TypeScript:
         | 
         | - https://redux.js.org/tutorials/typescript-quick-start
         | 
         | Note that we specifically tell you to use a
         | `PayloadAction<MyPayloadHere>` type, which results in well-
         | typed reducers and automatically generates strongly-typed
         | action creators to match.
         | 
         | If you're using Redux at all, you _should_ be using Redux
         | Toolkit to write your Redux logic.
        
       | mcluck wrote:
       | I could not disagree more. I absolutely love using TypeScript for
       | the libraries that I write and maintain.
       | 
       | > There's great documentation and blogs for end-developers but
       | there's very little for library-developers
       | 
       | Because there isn't a difference. I write my applications the
       | same way that I write libraries. TypeScript gives you the
       | flexibility to do that as well. If you want to use looser types
       | in your apps and in your libraries, go right ahead. Some people
       | may want more accurate types coming from your library. They'll
       | either improve them or pick something else. You aren't required
       | to appease them. I know that you stated that you reject this
       | notion but just now I was working on a relatively straightforward
       | React app and I've got a number of places where I'm doing
       | slightly more advanced types. If you aren't, you're probably
       | leaning on library developers to do that for you which is fine
       | but that's a choice.
       | 
       | > How do library developers debug their highly dynamic and heavy
       | use of conditional types, overloads?
       | 
       | I literally just make a number of assignments and hover over the
       | types to verify that they look right. There are better ways of
       | testing these that I'll get to later.                   //
       | Hovering over this will either show what I want or not
       | type ComplexFoo = ToComplexType<Foo>
       | 
       | Sometimes those types might look nasty and be difficult to parse
       | so there are ways to force TS to simplify them for inspection.
       | There are even libraries[1] that help with that.
       | 
       | > I spend a decent amount of time in the redux world so redux-
       | toolkit is a great library to see how types are done
       | 
       | Redux is an inherently very dynamic system. It's dynamic by
       | design. Capturing all of that dynamism in a strict system is
       | _hard._ `redux-toolkit` and related codebases are not
       | representative of the normal struggles for library developers.
       | Additionally, see point 1 about how you can choose how good your
       | types are. It would be technically correct for `createAction` to
       | just return                   interface Action {           type:
       | string           payload: unknown         }
       | 
       | They choose to make their types better so that developers don't
       | have to carry that burden.
       | 
       | > It's pretty common in style guides to never nest ternaries. In
       | typescript, that's the only way to narrow types based on other
       | types
       | 
       | I'm always open to syntactic improvements. If someone finds a way
       | to make the type syntax simpler without conflicting with existing
       | JS syntax then I will be very happy. That being said, the syntax
       | isn't so inscrutable as to be unusable. It just takes some
       | getting used to.
       | 
       | > It's not enough to test your types against the latest version
       | of the typescript compiler either, you also need to test against
       | previous versions
       | 
       | I disagree. The TypeScript team does a very good job of keeping
       | things backwards compatible. In the rare case that they don't,
       | your library is welcome to state that they only support certain
       | versions of TypeScript. This is a pretty standard problem with
       | any sort of dependency. Nothing about TypeScript makes this
       | particularly difficult. If you are going the extra mile to check
       | multiple versions of TypeScript then you're awesome.
       | 
       | > This new class of tests are in their infancy and there's a
       | baron wasteland of tools that are now deprecated or partially
       | maintained
       | 
       | My opinion: a lot of these tools become unmaintained because they
       | aren't necessary. Testing types is pretty easy. Include these two
       | functions and you're golden.                   function
       | generateType<TReturn>(): TReturn {           return null as
       | unknown as TReturn         }         function
       | assertType<TExpected>(value: TExpected) {}
       | 
       | Then testing your types is as simple as
       | assertType<Expected>(generateType<ComplexThing<number>>)
       | 
       | > When first attempting to contribute to a project, one must grok
       | the application logic as well as the type logic.
       | 
       | If you're running in to this then those people are doing it
       | wrong. Types should make impossible states impossible and do
       | their best to make the application logic the only thing that is
       | possible. If you understand the application logic, you understand
       | the type logic. They're one and the same.
       | 
       | > 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.
       | 
       | You shouldn't and you don't. Better documentation is always
       | better though and I agree that more in-depth first-party
       | documentation would be great.
       | 
       | [1]: https://millsp.github.io/ts-
       | toolbelt/modules/any_compute.htm...
        
       | robocat wrote:
       | Strong typing is most useful when used for library code. Users of
       | libraries can make their code as dynamic as they like.
       | 
       | Edit: I think their complaints apply to library development using
       | any language with types e.g. complex C++ template foo is most
       | useful for libraries? There is little in the article that is
       | specifically a problem with TypeScript.
       | 
       | * Good autocompletion. Great for developers using a library.
       | Somewhat self-documenting.
       | 
       | * Narrows down origin of errors. Great for library users and
       | library developers. Nobody wants uncertainty or finger pointing.
       | 
       | * Typing is a strong API contract. Breaking changes to the API
       | are more likely to break existing code. That is absolutely good
       | from the library user's point-of-view.
       | 
       | If a library developer wishes to deploy a highly dynamic API for
       | their library, they can just use "any", but expect most
       | developers to dislike the library.
       | 
       | Planning your types correctly, and designing the API carefully to
       | use types, does cost library developers a lot of time and
       | thought. But the library users get outsized gains because library
       | users usually hugely outnumber library developers.
       | 
       | > we are shifting complexity from end-developers to library
       | developers. This places a huge burden on us to be experts with
       | how typescript works.
       | 
       | That is to be expected. Good library code is hard to write. It
       | would be lovely if there were better resources to help library
       | writers, however it is a specialist role since library users
       | usually strongly outnumber library writers.
       | 
       | It appears to me that many of the design decisions in TypeScript
       | for the type system are there to help library developers.
       | Patterns of usage were recognised and then added to the type
       | system.
       | 
       | Disclaimer: not a TypeScript library developer.
        
       | benjismith wrote:
       | I liked the article, and I'm sympathetic to the overall point...
       | 
       | > The kind of hoops I have to jump through to get types "just
       | right" in a web app versus a library is dramatically different.
       | It's rare in a web app for me to need constructs like conditional
       | types, type operators, and overloads. As a library developer,
       | they are heavily used. These constructs are highly dynamic and
       | embed logic into your types. This leads into my next frustration
       | with typescript which is debugging.
       | 
       | I would have like to see some examples of "conditional types,
       | type operators, and overloads" in action, and an argument for why
       | these constructs are so much more prevalent in library code than
       | in application code.
       | 
       | I don't have a counter-argument, and I don't have any reason to
       | doubt the author's insight, but after reading the article I don't
       | feel like I have an increased understanding of the problem-space.
        
         | usrusr wrote:
         | It's been ages since my last dive into the js galaxy, but from
         | my outside perspective I read that as "the usual mess that we
         | like to do to provide awesome backwards compatibility when
         | introducing wildly changed API generations is very difficult to
         | sneak past the typechecker". Not sure that I read it correctly,
         | but it would certainly fit "conditional types, type operators,
         | and overloads".
         | 
         | Why are they more prevalent in library code: to make changes
         | non-breaking (or breaking, but fixable with minor tweaks). Of
         | course types can also be a solution to this problem, by
         | promoting consequences of incompatible change from runtime to
         | compile time, but that only holds if you can assume that all
         | calling code is type checked.
        
       | [deleted]
        
       | overgard wrote:
       | You only really need super complicated types to represent
       | existing very-dynamic javascript libraries. If you're starting
       | from scratch typescript is as easy and/or easier than javascript.
       | If you do have incredibly complicated types, it might be a sign
       | that you have a bad design. At work most of the typescript
       | libraries I wrote just ended up using interfaces and enumerated
       | strings and occasionally a union type. Sometimes I'd use "never"
       | to prevent certain things, but I can't say the types ever had to
       | get much more complex than that.
        
       | eyelidlessness wrote:
       | The problem with the premise of the article is that it
       | presupposes that a library's interfaces will, and ultimately
       | must, be complex. The great thing about TypeScript for library
       | development, _if you start with types_ , is that it strongly
       | encourages you to create simpler and less flexible interfaces.
       | That's not to say you don't sometimes need (or won't sometimes
       | choose) to use some of TypeScript's more complex features. But a
       | lot of the time you won't, or should think seriously about
       | whether your interfaces are becoming too complex.
       | 
       | Much of the complexity of TypeScript's type system is a direct
       | result of excessively dynamic interfaces in existing, untyped
       | JavaScript. Starting with types is a good way to catch that
       | early, rethink the interface, and either isolate it or avoid it
       | entirely.
        
         | matsemann wrote:
         | It was bad the first few years, with existing code being hard
         | to type because it was creatively written. But now that almost
         | everything is somewhat typed, JS has converged to a way of
         | writing code, and you should consider the tradeoffs before
         | choosing a different path than the current norm.
        
           | fullstackchris wrote:
           | I really _wish_ this were true, but go look at a vareity of
           | "modern" React projects. React is so flexible that I've seen
           | nightmare implementations of what could be reduced to just a
           | few short lines. Then people turn around and blame React as a
           | 'garbage framework'. Then if you talk about a much more
           | verbose and strict framework like Angular, people turn around
           | and complain "I can't do what I want with this overly
           | opinionated framework!"
           | 
           | Damned if you do, damned if you don't.
           | 
           | However, if you look behind the curtains of these infamous
           | gripes (some of them so often repeated they are cliche), 99%
           | of the time its people who just haven't spent enough time
           | around a certain framework or language and go create a rant
           | post (similar to OPs post)
           | 
           | You're free to love the tools and languages you use all the
           | time. But it's bad practice to go around bad mouthing tools
           | and frameworks you don't full understand or haven't used
           | productively.
        
         | david422 wrote:
         | We were trying to add types to some of our code. And the types
         | were getting ridiculously convoluted. And at that point it was
         | like - maybe it's not the types that are the problem, maybe
         | it's the interface that needs to be rewritten.
        
           | srcreigh wrote:
           | Care to share more details about code smell patterns you
           | noticed?
        
           | emn13 wrote:
           | The OP's example of createActions smells like such a
           | scenario. The way redux mashes up payloads and discriminators
           | makes it hard to type correctly - as in objects are not
           | simply composed, they're extended by adding properties, and
           | for no immediately obvious reason (which might be because of
           | my inexperience, granted).
           | 
           | Had redux been developed in typescript from the start, I
           | doubt it would have chosen this API. Then again, the current
           | version is already in typescript, so perhaps that optimism is
           | unwarranted; unfortunately my experience with it is years ago
           | already.
        
             | acemarke wrote:
             | This particular use case is trying to strongly type the
             | "Flux Standard Action" object shape. An object _may_ have a
             | `payload` field for its data, _may_ have an `error` field
             | that indicates this action represents an error, and _may_
             | have a `meta` field that provides additional descriptive
             | information.
             | 
             | This isn't part of the Redux core package, but it's a
             | convention the community adopted shortly after Redux became
             | popular. Redux Toolkit uses that as the standard structure
             | for an action object. `createAction` defaults to just
             | `{type, payload}`, but you can optionally provide a
             | callback that adds the other `meta` and `error` fields. So
             | yes, there's some additional complexity here because of the
             | optional field contents, and this type is telling TS what
             | the final type should look like based on which fields
             | exist.
             | 
             | Note that as an end user, you normally don't even call
             | `createAction` yourself - it's automatically called as part
             | of our `createSlice` API. What end users normally write is:
             | export const counterSlice = createSlice({         name:
             | 'counter',         // `createSlice` will infer the state
             | type from the `initialState` argument         initialState,
             | reducers: {           increment: state => {
             | state.value += 1           },           // Use the
             | PayloadAction type to declare the contents of
             | `action.payload`           incrementByAmount: (state,
             | action: PayloadAction<number>) => {             //
             | `action.payload` is now a `number`             state.value
             | += action.payload           }         }       })
        
         | throwaway0asd wrote:
         | Agreed! From the article:
         | 
         |  _How do library developers debug their highly dynamic and
         | heavy use of conditional types, overloads?_
         | 
         | Don't do that. It dramatically increases compile time and
         | results in a maintenance nightmare. Keep types primitive.
         | 
         | In my own code I use _type_ unions of primitives and /or named
         | types. For objects I use interfaces. With that I am able to
         | provides types for about 98% of my code. That left over 2% is
         | generally for extending the global Array type or extending an
         | event.
        
         | acemarke wrote:
         | I'll agree, and disagree.
         | 
         | I made a similar comment in 2019 about "TS pushes you towards
         | simpler APIs" [0], so I agree that it's both a good thing in
         | principle, and something TS usage leans towards in practice.
         | 
         | At the same time... JS _is_ a very dynamic language, library
         | design reflects that, and library code by its very nature must
         | account for the different ways that users will want to call it.
         | That causes library types to be much more complex than app
         | types.
         | 
         | [0] https://blog.isquaredsoftware.com/2019/11/blogged-answers-
         | le...
        
           | msie wrote:
           | Yikes! Why bend over so much for clients of the library?
        
           | bwestergard wrote:
           | "JS _is_ a very dynamic language, library design reflects
           | that"
           | 
           | I would argue it should not. If your API cannot be well
           | expressed in the Typescript type system, it is your API that
           | should change.
        
             | [deleted]
        
             | hmsimha wrote:
             | This is all well and good until you have to deal with other
             | code and other APIs in your library (results from network
             | requests, localstorage, untyped javascript libraries, etc.)
        
             | cogman10 wrote:
             | Yup.
             | 
             | Especially since types are documentation. If you are using
             | some meta dynamic type garbage, then I as a user of the API
             | am left to wonder "Ok, this takes <U, K extends keyof U>...
             | WTF is it expecting?"
             | 
             | This isn't to say that sort of thing is ALWAYS wrong, but
             | rather it should be the exception and not the rule.
             | 
             | You do types because constraints make code clearer. By
             | having a BF compiler in the middle of your template
             | definition, you've defeated the types and you might as well
             | put an `any` there with docs that say "here there be
             | dragons!"
        
             | naet wrote:
             | I'd rather not warp my whole project around a typing system
             | if I don't need to. I also might not be the owner of every
             | API I need to consume...
             | 
             | I think TS is great in certain cases- I love being able to
             | get intellisense info from TS. When I download a new
             | library and I get those helpful hints built right in from
             | the libraries TS adoption, I love it. Typescript in this
             | instance is reducing friction in my development workflow.
             | 
             | In my day job I work at an agency where we rapidly deliver
             | a lot of new web properties. I have used Typescript on
             | projects when it was a client requirement, but in our
             | environment using Typescript generally feels like it
             | overall adds friction and time to the project. Some team
             | member inevitably gets stuck spending a bunch of extra time
             | working with TS stuff: setting up the project with proper
             | rules, writing some complex interface to deal with some
             | random API we are either consuming or creating ourselves
             | (in which case it is usually under rapid prototype
             | development and needs to be updated constantly), etc.
             | 
             | I'm sure it is very helpful when working at scale with tons
             | of developers on a highly stable and established project,
             | but in my opinion it isn't generally applicable to every
             | web property that it would be better on Typescript; it
             | really depends on the scale of the project and the scale of
             | the team maintaining it.
        
           | folkrav wrote:
           | > JS _is_ a very dynamic language, library design reflects
           | that
           | 
           | This is mostly an issue if you're tacking on those types on
           | top of your existing design, rather than starting your design
           | from your types, no?
        
           | overgard wrote:
           | I don't think allowing users to call libraries in all manner
           | of crazy ways is a good thing at all, and I don't think
           | anyone would really be that put out by following a more
           | pythonic approach of just allowing one way to do something.
        
             | mmmpop wrote:
             | Yeah but you're writing libraries in Javascript, that ship
             | has sailed already. You're gonna just need a bit fat switch
             | statement to check your input types on every public
             | function you ever write.
        
               | overgard wrote:
               | It's never too late to stop making bad designs. I've
               | written plenty of (internal) libraries and if someone
               | asked me to put in a big fat switch statement I'd ask
               | them why they need it.
        
               | edgyquant wrote:
               | We're talking about typescript here
        
           | 323 wrote:
           | Python is also very dynamic, yet libraries rarely allow
           | arguments to be number/string/list/dict/function at the same
           | time.
           | 
           | Yet in JavaScript I see this all the time - pass a string
           | URL, but we also accept a function which will return a string
           | URL, or maybe an option object - { url: string, strict:
           | boolean }
           | 
           | So I think it's more of a culture thing.
        
             | nicoburns wrote:
             | Luckily these types are _not_ complex at all to type in
             | Typescript. You just enumerate all the options with a |
             | character separating them.
        
               | 323 wrote:
               | Right, but people compose. They take this union and put
               | it in another object, which also allows multiple types
               | for some field, and then they take that object and put it
               | in another one, and then you have a 10 line type
               | declaration for 3 fields.
        
             | nerdponx wrote:
             | Python libraries kind of used to do this more, but seem to
             | be doing it less now, in part because highly-polymorphic
             | interfaces are a huge ugly pain in the butt with the Python
             | type hinting system.
        
               | int_19h wrote:
               | What's so painful about writing                  foo: int
               | | str
        
               | nerdponx wrote:
               | It gets a lot more complicated than that. One of many,
               | many examples: https://github.com/pandas-dev/pandas-
               | stubs/blob/v1.4.3.22082...
               | 
               | In general, if someone with experience in a domain is
               | complaining that something is hard, it's safe to assume
               | that the thing is actually at least kind of hard, and
               | that they aren't just a crybaby about indentation and
               | formatting.
        
           | overgard wrote:
           | I think allowing people to call functions in a vast variety
           | of ways is a mistake. It's one of the things that frustrates
           | me the most about the javascript ecosystem, it's hard to
           | figure out the call signature for any given function because
           | there's like 15 different ways to call it. I'd argue it's
           | better to just have one well documented way. It's not even
           | like there aren't patterns for it, ie passing an "options"
           | object or something like that (although even then I think
           | that can be an antipattern unless there truly needs to be a
           | variety of options -- ie a db connection or something. Better
           | just to have multiple functions with specific purposes
           | instead of one mega function that does everything)
        
             | cogman10 wrote:
             | Agree, 100%. Make your function definition `foo(foo: Foo)`
             | not `foo<T extends comparable | int | Bar>(foo: T)`
             | Flexibility is the enemy of a type system and library
             | design in general.
        
           | danielvaughn wrote:
           | Case in point, over the weekend I tried creating an npm
           | package for a custom hook. Since it's public-facing, I wanted
           | to use TS so I could expose types for users.
           | 
           | Part of this hook is that users can pass in custom data. I
           | tried to create a type that was basically "an object of any
           | string key, with the type value". Gave up after like an hour
           | of banging my head against my desk. Maybe I'm new to TS, but
           | FFS it should _not_ be that hard to do.
        
             | jeremyjacob wrote:
             | Fortunately that's not too difficult:                 let
             | x: {[key: string]: Type}
             | 
             | But maybe the documentation is lacking here?
        
               | aylmao wrote:
               | For the record, this is equivalent to:
               | let x: Record<string, Type>
               | 
               | Pun intended.
        
               | [deleted]
        
         | refactor_master wrote:
         | I agree, and it's the same with Python. If you start with
         | static, type-safe first, then Python's flexibility ends up
         | being so horribly convoluted that you avoid it in favor of
         | simpler constructs. **kwargs? Massive union types? Super
         | flexible dicts? Rather not!
         | 
         | I'll define 50 strongly typed constructs over having to run my
         | code through 50 times to catch all the runtime bugs.
        
           | rocqua wrote:
           | I have found *kwargs useful for wrapper functions, where you
           | pass kwargs to some other, later defined, function.
        
         | dimmke wrote:
         | Yeah, Redux Saga is known for heavily using JS generators and I
         | wonder how much of this is just the relative obscurity of that
         | language feature for him. It's not exactly a straightforward
         | library.
        
         | dfabulich wrote:
         | Here's a perfect example. I maintain a simple Node library
         | designed to connect to Apple's App Store Connect API.
         | https://github.com/dfabulich/node-app-store-connect-api
         | 
         | It accepts, as a parameter, a URL for Apple's REST API. My
         | library handles authentication, and returns the parsed JSON
         | result, with a handful of tweaks to make the API more usable in
         | JavaScript.
         | 
         | Depending on which URL you request, you'll get different result
         | object back. You could get a single object in response, or an
         | array of objects, and the _type_ of returned objects is
         | different for each URL type.
         | 
         | How would you add TypeScript types to this API? Well, Apple
         | provides an OpenAPI documentation of all of their URLs, which I
         | could use to autogenerate types, but then, how would I handle
         | all of those types in response to the user's _string_ input?
         | 
         | Well, it turns out that TypeScript is so amazingly fancy that
         | you can write _very_ clever code to parse strings at _compile
         | time_ , extracting parameter types etc. from string literal
         | types. https://lihautan.com/extract-parameters-type-from-
         | string-lit...
         | 
         | The documentation explains how an API like this:
         | app.get('/purchase/[shopid]/[itemid]/args/[...args]')
         | 
         | can parse its parameters into a Request type with shopid,
         | itemid, and args[] array parameters. This would catch a bug if
         | you had a typo, e.g. "itmid".
         | 
         | But the code to do that looks like this:                   type
         | IsParameter<Part> = Part extends `[${infer ParamName}]` ?
         | ParamName : never;         type FilteredParts<Path> = Path
         | extends `${infer PartA}/${infer PartB}`         ?
         | IsParameter<PartA> | FilteredParts<PartB>         :
         | IsParameter<Path>;         type ParamValue<Key> = Key extends
         | `...${infer Anything}` ? string[] : number;         type
         | RemovePrefixDots<Key> = Key extends `...${infer Name}` ? Name :
         | Key;         type Params<Path> = {         [Key in
         | FilteredParts<Path> as RemovePrefixDots<Key>]: ParamValue<Key>;
         | };         type CallbackFn<Path> = (req: { params: Params<Path>
         | }) => void;                  function get<Path extends
         | string>(path: Path, callback: CallbackFn<Path>) {
         | // TODO: implement         }
         | 
         | Nifty, eh? But, as the article says: how would you test this
         | code? How would you debug it?
         | 
         | Clearly, I wouldn't do that. Instead, I'd write a script to
         | autogenerate individual methods, e.g. instead of
         | get(`apps/${appId}`) that returns a parsed JSON blob, I'd
         | autogenerate a getApp(appId) method that returns an App object.
         | 
         | But that API isn't any _simpler_ than the API I already have;
         | it 's just different.
         | 
         | And let's not forget that I'd have to write a script to
         | _autogenerate_ these methods (or just their types) based on
         | Apple 's OpenAPI specification, and now I have to _maintain_
         | that code, updating my @types /node-app-store-connect-api
         | definition every time Apple introduces a new URL you can
         | request. Testing and debugging _that_ is a challenge in its own
         | right.
         | 
         | And even if I did it, the complexity of my library just went
         | from a few hundred lines of glue code to 1,000+ lines of type
         | generation (plus tests for the generated types).
         | 
         | This is in no way worth it for me. As a library developer,
         | adding TypeScript types would make my life harder.
        
           | biorach wrote:
           | > And even if I did it, the complexity of my library just
           | went from a few hundred lines of glue code to 1,000+ lines of
           | type generation (plus tests for the generated types).
           | 
           | I would say that the increased complexity of your project
           | would accurately reflect the complexity of the underlying API
           | 
           | > As a library developer, adding TypeScript types would make
           | my life harder.
           | 
           | Yes, but it would mean that the various parameters passed to
           | your library would now be type safe, which sounds worth the
           | work
        
             | dfabulich wrote:
             | One clear moral of this story is that, as a library
             | developer, I need a lot more documentation and tooling
             | support from the TypeScript team.
             | 
             | That string-parsing thing isn't in TypeScript's
             | documentation at all; it's a random blog post. I'm lucky I
             | found it. TypeScript's site should document how to do this.
             | 
             | Furthermore, it should be easier to debug parsed stringly-
             | typed APIs using the `infer` keyword. Today, I just run the
             | compiler over and over again until it stops throwing
             | errors, and the errors are really not very helpful.
             | 
             | There should be a standard tool to help me test that I used
             | `infer` correctly, ideally helping me by fuzzing the code
             | to see if I allowed something I shouldn't have allowed, or
             | blocked something that I should have allowed, and to test
             | against various versions of TypeScript.
             | 
             | Narrowing types shouldn't be done just by nesting ternary
             | expressions. As the article notes, "It's pretty common in
             | style guides to never nest ternaries. In typescript, that's
             | the only way to narrow types based on other types. It's a
             | mess!"
             | 
             | Lastly, this idea doesn't sit right with me:
             | 
             | > I would say that the increased complexity of your project
             | would accurately reflect the complexity of the underlying
             | API
             | 
             | I designed my library so it doesn't need to incorporate
             | that complexity. Users of my API have to learn the API, but
             | they can learn it from Apple, by reading Apple's
             | documentation, and by inspecting the objects that Apple
             | actually returns (rather than what their OpenAPI
             | specification _says_ they 'll return).
             | 
             | Incorporating that complexity into my library will make my
             | library harder to work with, more difficult for others to
             | contribute to my library. As the article says, "Types make
             | it much harder to maintain a js library, and especially
             | difficult to contribute to them."
             | 
             | TypeScript is forcing me to duplicate the API's complexity
             | in my code. Even if you think that "sounds worth the work,"
             | I think you have to agree that the work load is much higher
             | than the work I've already done.
        
           | overgard wrote:
           | > Depending on which URL you request, you'll get different
           | result object back.
           | 
           | This sounds awful to me. Why wouldn't it be separate
           | functions? I absolutely would not want that at all as an end
           | user, I'd find it confusing and frustrating.
        
             | dfabulich wrote:
             | Because in order to have separate functions, I'd have to
             | define separate functions for every function that Apple
             | supports, i.e. I'd have to autogenerate the list from
             | Apple's OpenAPI specification.
             | 
             | Furthermore, if Apple introduces a new function, I'd have
             | to release a new version of my library to support it.
             | Today, my library can handle any object Apple's API
             | supports, without upgrading.
             | 
             | As it stands, the API works like this. It's fine.
             | const {data: app} = await read('apps/123456')         const
             | {data: apps} = await read('apps')         const {data:
             | [firstApp]} = await read('apps?limit=1')
        
               | overgard wrote:
               | Swagger can generate that sort of thing. I guess I don't
               | know what things your library provides so I don't want to
               | bash it, but having a generic read/get that doesn't
               | provide types doesn't seem like a big improvement over
               | just using a general purpose tool like axios or fetch.
        
         | ricardobeat wrote:
         | Agree 100%. Redux et al are terrible examples where complexity
         | is through the roof, these libraries were written way before
         | TypeScript was the norm. You can eliminate almost all of that
         | by having simpler interfaces, but that requires a different API
         | built with types in mind.
        
       | mckravchyk wrote:
       | I agree that there's much more type wrangling when developing a
       | library vs. typing regular app code. I don't mind it all though.
        
       | rikroots wrote:
       | My personal experience as a library developer, who has written my
       | library in JS, not TS ...
       | 
       | TS is an excellent choice for a lib dev starting a new project
       | today. I can see the advantages of using TS for the library code
       | - in particular for a library that gets popular and welcomes
       | contributions from other developers. However TS is a nightmare
       | for someone like me who: 1. started writing the library 9 years
       | ago; 2. has let the library get "quite" big; and 3. has only
       | learned to use TS in the past year (for the day job) and is
       | nowhere near to becoming a types expert.
       | 
       | I've had experience of people suggesting I rewrite the library in
       | TS. Sometimes those suggestions have been quite 'evangelical' in
       | their tone. As an (essentially) solo developer I just don't have
       | the time, capacity or willingness to do that work - however much
       | the end results might please others.
       | 
       | I also understand that having type definitions file for the
       | library's interface is, nowadays, a critical factor if the lib
       | dev wants others to use the library in their projects. But
       | writing a .d.ts file for a large, mature repo to at least help
       | those potential users can quickly turn into a World of Hurt. I
       | know this because I've done that work[1] and I never want to do
       | it again.
       | 
       | As much as I know that TS is a Force for Good in the JS coding
       | world, there are days when I detest it!
       | 
       | [1] - link to the Scrawl-canvas .d.ts file on GitHub -
       | https://github.com/KaliedaRik/Scrawl-canvas/blob/master/sour...
        
       | simonsarris wrote:
       | I am really surprised by this guy's opinion. I make GoJS
       | (https://gojs.net/), a diagramming library written in TypeScript
       | with 68k weekly downloads on NPM. The project began in 2011 and
       | we converted it to TS in 2018. It's been a _huge_ plus. The sole
       | downside was the initial time it took during conversion, but even
       | in doing so we caught bugs with incorrect input types,
       | documentation mistakes, bad range enforcement, etc.
       | 
       | On our end, it enforces type safety better than the Google
       | Closure Compiler. There has scarcely been a problem with type
       | complexity that was not ultimately our fault. Just a couple minor
       | things that TS amended later. For that matter the TS experience
       | has only gotten better, generally.
       | 
       | On our users end, we can now give them a .d.ts file that's much
       | richer and easier for us to produce to aid their autocompletion.
       | And we can use that .d.ts file to ensure that all the methods we
       | intended to expose/minify are getting exposed. The advantages
       | with the .d.ts and documentation make it feel almost essential to
       | me for library developers to consider TS.
       | 
       | TypeScript has only made debugging easier, much easier since it
       | catches errors at time of typing unlike the closure compiler. The
       | sole exception is that debugging is a bit slower since I have to
       | transpile instead of just refreshing the browser. But I have tsc
       | set to compile a relatively unminified version of the JS. But if
       | the slowness gets to me, I can just edit the JS output until I
       | solve the issue, and then carry those edits over to the TS. This
       | has never felt like a problem, though maybe his library is
       | significantly more complicated.
       | 
       | It is very possible that my opinions are colored by using the
       | Google Closure Compiler for so many years for type enforcement,
       | long before TS, which has forced a level of discipline that may
       | be unusual to JS programmers. But it seems like the problems are
       | not with TS, but with the labors of architecting coherent and
       | consistent APIs. But I am not familiar with the author's work to
       | really judge.
       | 
       | Feel free to ask me anything if you have questions about library
       | design + TS.
        
       | Bellend wrote:
       | "I don't know how to make my library typesafe so everyone else is
       | the problem".
       | 
       | It's a fucking boring topic that has been done to death since
       | 2012. Get this deleted guys.
        
       | bigbillheck wrote:
       | I'm not a ts/js person but > we are shifting complexity from end-
       | developers to library developers
       | 
       | this is exactly the point of a library in any language. The
       | library authors do the heavy lifting precisely so that the end-
       | users don't have to!
       | 
       | And again, this isn't my domain, but when I read stuff like > How
       | do library developers debug their highly dynamic and heavy use of
       | conditional types, overloads? or > Because types can be generated
       | from other types and the highly dynamic nature of those types,
       | there's a new class of tests that are required for any serious
       | typescript project: testing your types. or > I spend more time
       | tweaking types than I do writing library code. what I'm thinking
       | is "Doctor, it hurts when I do this".
        
       | henning wrote:
       | > In effect, we are shifting complexity from end-developers to
       | library developers
       | 
       | That is the whole reason to use libraries.
       | 
       | This blog post shows that types make development more enjoyable
       | by shifting the complexity burden to library authors, who are
       | vastly fewer in number than library users. It is a clear win.
        
       | [deleted]
        
       | mpolichette wrote:
       | I can empathize with the author here that types can be very
       | challenging to get right, especially with high amounts of
       | dynamism.
       | 
       | However, I think that saying it is "terrible for library
       | developers" is a bit far. I think its terrible for developers who
       | want to make use of advance types... which ultimately doesn't
       | depend if you're a library dev at all.
       | 
       | It boils down to: "Typescript learning curve gets really steep
       | after the basics"
       | 
       | The author mentions they help maintain redux-saga. I don't want
       | to dig on them, but from my personal experience, that library
       | takes you down quite an opinionated application path with
       | complexity inherit to it. Heck, just making types for redux was a
       | pain, let alone adding async and additional composability.
       | 
       | My take is that the author has chosen complex tech to work with,
       | and that influences the complexity in their types.
        
       | jasonhansel wrote:
       | Even better: because of TypeScript's many known (and unfixable)
       | soundness bugs, adding types doesn't even guarantee that there
       | will be no type errors at runtime.
        
       | shadowofneptune wrote:
       | I would have liked to see examples. My own instinct here would be
       | to use simpler types, but examples could show why that isn't
       | desirable.
        
         | ryanmcbride wrote:
         | They probably just keep running into issues that seem really
         | trivial but are unable to find info on fixing. It's the reason
         | I put off writing things in typescript until relatively
         | recently.
         | 
         | In that vein, if anyone here can tell me how the hell you
         | functionally map over a typed object by key in typescript I'll
         | be eternally grateful.
         | 
         | I always wanna do something like
         | Object.keys(typedObjName).map(...) but that doesn't work.
         | 
         | It's so stupid and small and minor and it's never really
         | prevented me from getting something done but it drives me out
         | of my mind that I still can't find any clear documentation on
         | how to do it.
        
           | nkingsy wrote:
           | Object.entries(typedObjName).map(([key, value]) =>
           | `${key}${value}`)
        
           | cuddlecake wrote:
           | Do you have an example for what you want to do, with an
           | object before and after mapping? I don't understand what you
           | mean by "functionally map over a typed object by key"
        
             | shadowofneptune wrote:
             | I think I have run into a similar issue. I wrote a lexer in
             | Typescript. It is table-based, as is the parser that runs
             | after it. The type for the table looks something like this:
             | type TokenTable<T> = {             plus: T,
             | minus: T,             bang: T,             parenOpen: T,
             | //etc.         };
             | 
             | I also have a type defined as 'type TokenID = keyof
             | TokenTable<unknown>;' this makes it possible to check if a
             | string is a valid key at compile-time. The innermost loop
             | of the lexer is a for..in loop. This gives you the keys of
             | the object. One problem: if you try to apply the TokenID
             | type to the loop variable, you get this message: "The left-
             | hand side of a 'for...in' statement cannot use a type
             | annotation." Because of the design of JavaScript, TS cannot
             | give object keys any other type but 'string', even though
             | this type seems like a clear match.
             | 
             | To get the typechecking back on the keys, you either need
             | to declare the loop variable outside of the loop itself, or
             | use type casting like this:                   let token =
             | "";         let id: TokenID | undefined;         for (let
             | key in patterns) {             const match = patterns[key
             | as TokenID].exec(substring);             if (match &&
             | (match[0].length > token.length || key == "EOF")) {
             | token = match[0];                 id = key as TokenID;
             | }         }
             | 
             | Neither is particularly clean.
        
               | lozenge wrote:
               | Can you define a const array with type Array<TokenId> and
               | use it every time you want to loop through these keys?
        
               | shadowofneptune wrote:
               | That's a possibility, yes. This is the only time in the
               | program that a token table is iterated through, however.
               | Most of the time a table is consulted to pursue an action
               | in the parser. For example, there's a function table for
               | when a statement is encountered, another for when an
               | expression operand is encountered, etc. Each entry in the
               | table is either an error message or code which completes
               | the parsing of that statement. The awkwardness above is
               | excusable when it is encountered so little. When writing
               | expression-heavy stuff like
               | 'Object.keys(typedObjName).map(...)' it's more of a
               | problem.
        
               | gnud wrote:
               | You can approximate something by defining the valid
               | TokenTable keys as an enum, and using a mapped type for
               | the actual TokenTable type.
               | 
               | There's some boilerplate in the definition, but it's
               | fairly clean and non-repetitive. And easy to use in the
               | "client code".
               | 
               | https://www.typescriptlang.org/play?#code/KYOwrgtgBAKg9ga
               | 1AS...
        
               | shadowofneptune wrote:
               | You put some real effort into the example. It's the
               | opposite approach of how I did it, yet works just as
               | well. Thanks!
        
           | badlucklottery wrote:
           | > I always wanna do something like
           | Object.keys(typedObjName).map(...) but that doesn't work.
           | 
           | Is this a type space/value space thing? Like Object.keys(...)
           | is always a string[] instead of Array<keyof TypedObj> like
           | you might expect?
        
         | qudat wrote:
         | Agreed. I updated the blog to add an example. Copy/pasting
         | here:
         | 
         | I spend a decent amount of time in the redux world so `redux-
         | toolkit` is a great library to see how types are done
         | *correctly* in a real codebase. To be clear, they do a
         | fantastic job with types, but the level of complexity is pretty
         | startling.
         | 
         | https://github.com/reduxjs/redux-toolkit/blob/master/package...
         | 
         | That is just one example but the codebase is riddled with
         | complex types. Also, when you look around, note the amount of
         | types vs actual code.
         | 
         | It's pretty common in style guides to never nest ternaries. In
         | typescript, that's the only way to narrow types based on other
         | types. It's a mess!
        
           | shadowofneptune wrote:
           | Thank you.
        
           | nkingsy wrote:
           | Along the lines of "just use a dynamic language", we use
           | unknown and any type for a lot of our internal stuff like
           | this.
           | 
           | Strongly typed shell, whatever works core.
        
         | meheleventyone wrote:
         | I suspect it's a case of bolting on types to soupy JS but would
         | also love to see some examples. I agree with the other
         | commentators that part of the point of a library is to suck in
         | complexity but if you're regularly doing high level type kungfu
         | there are probably bigger structural issues.
        
       | brap wrote:
       | The examples they provide from Redux are insane, and they're
       | saying it's "types done right"? What is it about TypeScript that
       | you get these insanely verbose types? I have never seen anything
       | remotely like this in other typed languages e.g Java. Maybe lack
       | of overloaded functions?
        
       | progx wrote:
       | "to spend less time making tsc happy" signed!
       | 
       | I feel i spent more time with that than with the development of
       | the code ;-)
        
       | qbasic_forever wrote:
       | I wish there were a formal subset of typescript that was go-like
       | in design. Interfaces, structs, and simple types but not all the
       | class-based object oriented complexity. A focus on composition
       | instead of inheritance.
       | 
       | I like typescript but really worry that it's fueling a boom in
       | unnecessary complexity and architecture. Are we going to look
       | back at monster typescript codebases in the same way we look at
       | monster java codebases riddled with abstract and redundant layers
       | of complexity because the "design patterns" say to do it?
        
       | thdxr wrote:
       | A great counter point to the thinking in this article is this
       | snippet by Dan Abramov: https://overreacted.io/what-are-the-
       | react-team-principles/#a...
       | 
       | Yes we need better tools as library authors but absorbing this
       | complexity is our job. The more we take advantage of what
       | Typescript can do the fewer types the end users deal with and the
       | better experience they have.
       | 
       | Making your library's internals simple should be a non-goal. The
       | number of people dealing with it will be a fraction of the people
       | dealing with codebases using your library.
        
       | latchkey wrote:
       | This is a matter of opinion.
       | 
       | I wrote a set of React components that gets 46,000 npm downloads
       | a month and typescript is a godsend. My library is a bridge
       | between two heavily popular projects, so my dependency tree is
       | fairly intertwined. The library solves a real user problem and
       | does it efficiently. It isn't totally perfect, but it covers 98%
       | of the use cases.
       | 
       | I wrote comprehensive tests as I developed everything. I have
       | good documentation. I have an example app people can play with. I
       | have codesandboxes. Yes, it was a lot of work, but that's what
       | building products is all about.
       | 
       | I've maintained this library for over 3 years now. I've upgraded
       | it many times as the underlying dependencies have changed. A few
       | times with backwards incompatible changes. I get outside
       | contributors doing great work.
       | 
       | I do releases on a regular basis, without worry, because my test
       | suite is that good and because I know that if the underlying
       | types change, tsc will catch that too. These things have saved me
       | countless hours of work finding bugs and fixing issues.
       | 
       | 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? Come
       | on.
       | 
       | Learn how to use types. Learn how to use a professional IDE, like
       | IDEA, that shows you what's wrong as you develop. Learn how to
       | write tests. Learn how to use a debugger. These are all things
       | any decent developer should know and practice on a regular basis.
        
         | gkiely wrote:
         | > Typescript is the correct solution. What is the alternative?
         | Just using JS and letting things break without knowing it? Come
         | on.
         | 
         | Surely that's not the only alternative.
         | 
         | Here's a few off the top of my head:
         | 
         | - Being able to write complex types in a syntax that more
         | closely resembles JavaScript. Using Array length for counting
         | or nested ternaries for if logic gets old fast.
         | 
         | - Being able to debug types, not console output, a real
         | debugger. See:
         | https://twitter.com/MarcJSchmidt/status/1539787500788613120
         | 
         | - More comprehensive documentation on writing advanced types
         | 
         | - A typescript specification
        
           | dvt wrote:
           | > Being able to write complex types in a syntax that more
           | closely resembles JavaScript
           | 
           | Yikes. This is how you end up in preprocessor hell. Macros
           | are generally _not_ a good thing. (Fyi: TS types are already
           | Turing-complete which is arguably a mistake.)
           | 
           | > Being able to debug types, not console output
           | 
           | Eh. It's not like Java has a "type debugger." Why is this
           | needed? Why are your types so complex? Weird ask.
           | 
           | > More comprehensive documentation on writing advanced types
           | 
           | Really beating the same drum here.
           | 
           | > A typescript specification
           | 
           | What does this mean? We have a pretty clear typescript
           | spec[1].
           | 
           | [1] https://github.com/microsoft/TypeScript/blob/main/doc/spe
           | c-A...
        
             | gkiely wrote:
             | > Yikes. This is how you end up in preprocessor hell.
             | Macros are generally not a good thing.
             | 
             | Not suggesting macros, rather an alternative way to define
             | types. A function that accepts and returns types. Seeing as
             | typescript has a JavaScript interpreter I thought it might
             | be feasible but I'm just spitballing it.
             | 
             | > Eh. It's not like Java has a "type debugger." Why is this
             | needed? Why are your types so complex? Weird ask.
             | 
             | I don't buy this argument. Writing any type of complex
             | types with recursion, arrays or ternaries sucks and it will
             | take more than "this is how Java does it" to convince me
             | otherwise. I like to debug with a debugger, not my head.
             | 
             | Take a look at the examples in the article to see some
             | complex types and read some tweets from library authors
             | complaining on twitter. I can find some examples if you're
             | interested.
             | 
             | > We have a pretty clear typescript spec
             | 
             | I hadn't actually seen this and it looks interesting but
             | seems pretty out of date, Typescript 1.8?
        
           | latchkey wrote:
           | > - Being able to debug types, not console output, a real
           | debugger. See:
           | https://twitter.com/MarcJSchmidt/status/1539787500788613120
           | 
           | That is really nice. I'd love to see that live.
        
         | david422 wrote:
         | > Just using JS and letting things break without knowing it?
         | 
         | And if types change, and it still works, it's basically working
         | by accident.
        
           | oivey wrote:
           | That's a pretty underspecified comment for a static typing
           | enthusiast. You're referring to polymorphism.
           | 
           | And to be completely clear: you can have safe polymorphism
           | without type hierarchies via things like type inference.
        
           | latchkey wrote:
           | If types change, it doesn't compile and I can't do a release
           | in CI.
           | 
           | I don't consider that working.
        
             | david422 wrote:
             | I'm agreeing with you here. If you use plain JS and change
             | the underlying types that are being passed around, but
             | things are still working, you're basically getting lucky.
        
               | latchkey wrote:
               | Ok, got it. Apologies.
               | 
               | If you write comprehensive unit tests for your
               | javascript, that should effectively also work too... this
               | is what ruby developers pretty much had to do.
               | 
               | We can see that over time based on the decline of ruby
               | popularity in general, that fell out of style though. It
               | is too easy to make a mistake and everyone was just
               | duplicating what a compiler does for us.
               | 
               | I can't tell you how many hours I would sit there pair
               | programming with other ruby developers just trying to
               | figure out what 'type' an object is. Mind boggling, but
               | it was good to get those high paid consulting hours.
        
           | arinlen wrote:
           | > _And if types change, and it still works, it 's basically
           | working by accident._
           | 
           | So the Liskov substitution principle now passes off as
           | "working by accident"?
        
         | 3qz wrote:
         | Do you get paid for any of this?
        
           | latchkey wrote:
           | Not directly. I've been developing open source for almost 30
           | years now (I co-founded Apache Java/Jakarta). I consider that
           | the experience gained from developing extra curricular
           | projects pays for itself in other ways. For example, I've
           | never had to interview or even look for a job and I've never
           | been laid off.
        
             | mmmpop wrote:
             | Invest in yourself and you'll never work a day in your
             | life.
        
         | somenewaccount1 wrote:
         | Tell me that you didn't read the article without telling me you
         | didn't read the article.
         | 
         | tl;dr: the author loves typescript and thinks it brings a ton
         | of value to the end user. He also agrees that he wishes he knew
         | more about complex types which are needed in libraries - and
         | that's his primary gripe specifically as a library developer -
         | is that the knowledge base for library developers is very
         | sparse.
        
           | remram wrote:
           | Tell me you didn't read the HN guidelines without telling me
           | you didn't read the guidelines.
           | 
           | > Please don't comment on whether someone read an article.
        
             | nightski wrote:
             | There should be another guideline about commenting on
             | whether someone read the guidelines.
        
             | somenewaccount1 wrote:
             | to be fair to me, i did read them....i just don't care that
             | much.
        
           | latchkey wrote:
           | > TypeScript is terrible for library developers
           | 
           | That's the title. That's the whole premise of the post.
           | 
           | > his primary gripe specifically as a library developer - is
           | that the knowledge base for library developers is very
           | sparse.
           | 
           | No, it isn't.
        
             | mattmanser wrote:
             | It's the first of his 5 gripes. There's only 5 of them and
             | they're titled. A few of the other gripes also mention the
             | lack of documentation, so it definitely appears to be his
             | main gripe.
             | 
             | I agree with GP, you don't seem to have read his article
             | and knee jerked a reply.
             | 
             | And all you have to do to prove your point is to link some
             | great documentation explaining how to make complex types
             | for libraries and how to test them.
             | 
             | Which you haven't.
        
               | latchkey wrote:
               | What I said is that I call bullshit on his 5 points. None
               | of them are valid and they are just opinions of a
               | developer who hasn't put the effort in and isn't
               | providing concrete examples of what is actually wrong. I
               | actually find it difficult to understand why this is even
               | on the front page of HN, maybe it is a slow news day?
               | 
               | I feel that my point is proven with a successful multiple
               | year open source Typescript library.
               | 
               | Great documentation? That's subjective. I'd start with a
               | deep reading of the official docs as they are quite good:
               | https://www.typescriptlang.org/docs/
        
               | westoncb wrote:
               | > I feel that my point is proven with a successful
               | multiple year open source Typescript library.
               | 
               | That only proves that it's _possible_ to do, not that the
               | language facilitates it or that certain modifications to
               | the language /ecosystem wouldn't greatly improve the
               | process. The point of the article is the latter, not the
               | former.
        
         | andrewmcwatters wrote:
         | What package do you publish? https://github.com/lookfirst/mui-
         | rff?
        
         | Shorel wrote:
         | I agree so much with you.
         | 
         | I would even say: most libraries used for just about anything,
         | are written in C/C++/Java, all of which are typed languages.
         | 
         | The post's author simply lacks the education and experience to
         | actually have a valid opinion on this topic.
        
         | auggierose wrote:
         | Good for you. I was somewhat disappointed with TypeScript in
         | the beginning, because I tried to use the type system like I
         | would use it in Swift or Scala. That's not really a good idea.
         | If instead, you view TypeScript as a smoother implementation of
         | JavaScript + JSDoc, then it becomes a really powerful tool,
         | which I like more and more!
        
         | hmsimha wrote:
         | > 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 wrote:
           | Do you have an example? I've seen TypeScript handle crazy
           | nested types (and generics) with ease.
        
             | hmsimha wrote:
             | Perhaps 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.c
             | om/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.
        
           | drewpayment wrote:
           | You 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 wrote:
             | Yes, 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/t...
        
               | arinlen wrote:
               | > _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 wrote:
               | JavaScript
        
       | olliej wrote:
       | > There are a lot of reasons why typescript sucks for library
       | developers, but at the end of the day it reduces developer
       | productivity. In effect, we are shifting complexity from end-
       | developers to library developers.
       | 
       | Yes. The point of making a library is almost entirely to take the
       | complexity load from developers.
        
       | simlevesque wrote:
       | If your library was built without thinking about types, yeah
       | it'll be ugly when you add them after.
        
       | z3t4 wrote:
       | Libraries need good documentation with reference and code
       | examples. What libraries don't need is complex type annotations
       | that wont make it into the compiled code anyway. The worst
       | documentation I've ever seen is the one that use the types as
       | documentation, looking at you the Language server interface.
        
       | Loeffelmann wrote:
       | > It's pretty common in style guides to never nest ternaries. In
       | typescript, that's the only way to narrow types based on other
       | types. It's a mess!
       | 
       | I really wish there was a kind of type builder where I can use
       | ifs and switches to build complex types. For me that would
       | already take away a lot of the pain of maintaining and writing
       | complex types.
        
       ___________________________________________________________________
       (page generated 2022-08-23 23:00 UTC)