[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)