Hacker News new | ask | show | jobs
by shroom 2227 days ago
Any pro typescripter have any tips for how best use it in existing Node/React codebase?

This article mentions material-ui and styled-components. My experience with typescript has been cumbersome with these libraries. A lot of time is spent figuring out what ”types” to return. I find this very difficult in most cases and not fun at all.

I figure there must be something wrong in my approach or in my ts config (no explicit any).

In short. How to best handle styled-components/material-ui/other library in ts environment? Or is typescript just to much overhead in these usecases?

9 comments

> How to best handle styled-components/material-ui/other library in ts environment?

I’m using Blueprint 3 in TS environment and it’s really nice to get feedback about, say, wrong props passed to one of its styled components right as you type.

> My experience with typescript has been cumbersome with these libraries. A lot of time is spent figuring out what ”types” to return. I find this very difficult in most cases and not fun at all.

Whether your code is statically typed or not, you generally have to be aware of which data structure goes where—otherwise it’s easy to break things.

When code is statically typed, most things break at compile time, enabling your IDE to give immediate feedback. I find the flow much more pleasant if I don’t have to hunt down runtime errors resulting from using a library (BP3 or anything else) in a wrong way.

There is some upfront effort in specifying types explicitly where compiler can’t figure it out or does so mistakenly; how fun that is might depend on your personality and established habits. In addition, I believe it heavily depends on tooling—I like using TS in VS Code environment, but TS with Vim (which I’ve been using for many years) was somewhat of a hell.

Once in a while I come across an obscure library without TS typings, in which case I would generally provide a `.d.ts` file for it, starting with a catch-all `any` type for its exports, later potentially specifying it further if useful and feasible. This is by far the least fun part.

> Once in a while I come across an obscure library without TS typings, in which case I would generally provide a `.d.ts` file for it, starting with a catch-all `any` type for its exports, later potentially specifying it further if useful and feasible. This is by far the least fun part.

If you have time, I have a question. Note that my professional exposure to Typescript projects is minimal.

I've built a Javascript library which lives in GitHub, NPM, etc (I won't spam the link). It is entirely module-based Javascript.

My understanding is that while Typescript-based projects can use non-TS JS libraries (because TS is a superset of JS), using that library would require more work - initial code, and maintenance - than using an equivalent library either written in TS, or one that includes a TS Declaration File.

If yes to the above, then - given that I'm serious about my library and want other people to use it if it meets their project needs - I assume I need to write a .d.ts Declaration File (or maybe several?) for it.

- How much initial work are we talking here: weeks? Months?

- After the initial work, how much additional work is required to maintain the .d.ts files(s)?

- How much damage could a poorly written .d.ts file do to a library?

- I've seen the Microsoft documentation[1] and ... this looks like a massive amount of work for my library. I know how popular and useful TS has become but, at the end of the day, would I be wasting my time doing what would be, for me, unpaid work?

[1] https://www.typescriptlang.org/docs/handbook/declaration-fil...

Sort of... Your maintenance burden just becomes higher as a consumer of the library because you can't rely on the compiler to tell you if something goes wrong. But if you have a good testing harness you'll probably figure that out eventually anyways.

To generate typings for your library, you can start with converting from jsdoc if you have docstrings with types, or if you want to infer types at runtime during a testing suite you can use something like https://github.com/microsoft/dts-gen

I've never used dts Gen but it looks promising as a starting point. But as an additional point, only the public functions and classes really need type declarations, so unless you export all of your functions that could cut down on the burden to write up the types.

I've written a lot of .d.ts files, hopefully I can help.

> My understanding is that while Typescript-based projects can use non-TS JS libraries (because TS is a superset of JS), using that library would require more work - initial code, and maintenance - than using an equivalent library either written in TS, or one that includes a TS Declaration File.

More work, yes, but often not a lot of it depending on need. (You can just tell Typescript some modules are `any` and treat them as a black box.)

It's not so much "more work" as it is "less confidence". If you are looking for trust from TS developers in your library, a TS Declaration is API documentation directly in their IDE that does basic sanity/usage checks for them.

> How much initial work are we talking here: weeks? Months?

I find it is usually in the order of hours. The variables are the size of your library and in particular its external API surface and how well your API is already documented. A .d.ts file is just another sort of API documentation. If you've got good JSDOC comments, for instance, that does a lot of the work already. In some cases you can get a .d.ts file for "free" from Typescript if you follow a couple patterns in your JSDOC comments and run Typescript on your JS files. (It might also give you errors where your code doesn't agree with type hints in your JSDOC comments, which you may or may not find useful to discover potential bugs or edge cases in your code.)

> After the initial work, how much additional work is required to maintain the .d.ts files(s)?

It depends on your API changes, especially breaking changes. I've told a number of projects to feel free to @ me when they make API changes and I'll PR a .d.ts update and it's rarely more than a couple minutes to look at what their breaking API changes are and what update what needs to be updated.

> How much damage could a poorly written .d.ts file do to a library?

Again, it's mostly a "loss of confidence" style issue. A poorly written .d.ts isn't going to stop a Typescript user from using your library, it's mostly at worst just going to get in their way and they have to use an escape valve (such as a cast to `any` telling Typescript to get out of the way) and it is no worse than having no .d.ts.

Again, if it helps to think of it as API documentation: a bad .d.ts file is like finding an out of date API document for an old version of the API. It can still offer sign posts to what the expected API is supposed to be, it just requires the user of the library to distrust that documentation and either read the source directly or find other documentation sources (StackOverflow or example code from other users, for example).

> I've seen the Microsoft documentation[1] and ... this looks like a massive amount of work for my library. I know how popular and useful TS has become but, at the end of the day, would I be wasting my time doing what would be, for me, unpaid work?

As with any open source work it is a trade-off and you need to make that determination based on what you are comfortable with maintaining. That's why whenever I've written .d.ts files for libraries I don't maintain, I make sure to explain the maintenance effort, often volunteer to try to do my best to keep it maintained, and also realize and respect that not every library wants to maintain their own .d.ts.

It's also why you don't necessarily need to build the types yourself if you aren't (yet) comfortable learning Typescript or switching to Typescript. (Aside: I find that curiosity in adding your own .d.ts file is often a baby step for learning where Typescript helps you in making a better library, and some of the projects I first helped maintained a .d.ts eventually learned and switched to Typescript themselves.)

If you can find a user of your library that uses Typescript, they may already have a partial type definition you can start from and/or be willing to help you write/maintain the type definitions.

There is a central repository called DefinitelyTyped that happily maintains shared type definitions for a large number of libraries that don't intend to or cannot own and/or maintain their own type definitions. It publishes to an @types/ organization on npm. Your library may already be on there if you have enough users. In that case you also already have a path to starting with something someone else wrote, and GitHub username's to contact and reach out for additional help. If you aren't already in DefinitelyTyped and don't know specific users of your library that use Typescript who could help contribute, there are Definitions Wanted tags in DT's Issue tracker on GitHub and sometimes you can find help from DT's large number of contributors.

Hope that helps.

On a tangent, but do you have any recommended readings specifically on writing .d.ts? I don't have a lot of experience on typed languages and I'm not too comfortable writing them, but I realize the value of having them on my project.
I would suggest starting on the experience with typed languages in general first. Starting with the beginning of the Typescript handbook and trying working with things for instance directly in the Typescript Playground. You are going to want all of the basics when writing a .d.ts, and sometimes you aren't going to need much more than the basics.

The other thing I'd recommend is if your focus really is just specifically on writing .d.ts files, and you aren't expecting to use more Typescript directly yourself: get more familiar with and start regularly using JSDoc comments. Typescript in JS mode already understands and can do a lot with JSDoc comments. As I mentioned, it will sometimes give you a fully formed .d.ts if you ask and you have enough JSDoc comments, you'll even get the advantage of some of its type analysis in your project (not as much as if you were writing Typescript directly, but more than just a linter will do). If you use an editor such as VSCode where the JS experience is already backed by the Typescript language service you'd start to see some things almost Typescript-like light up in your development experience as you add more JSDoc comments (maybe even to help explain why you might want to use more Typescript directly).

https://en.wikipedia.org/wiki/JSDoc

My focus is not specifically on just writing .d.ts, but mostly being capable enough to adapt JS libraries and type them for my needs. Thanks for the recommendation, will muck around with TS for now.
> Hope that helps.

Thank you! This is a fantastic response (now bookmarked).

I think it best not to do the work now - the library is still evolving and keeping the basic documentation up-to-date (inline comments, but not in JSDOC format) is already an overhead. But this is something I'll need to address at some point. I'll add it to the library's roadmap.

Oh thanks! Yes my "problem" is mostly around what types to return or rather how to find them. To be aware that separate types package must be installed is definitely something to get into habit.

I wasn't aware of the `.d.ts` files. It sounds a lot like what I was looking for! Thanks again for your feedback and help :-)

Most of the time you can just `npm install -D @types/package-name` to get the types for that package. E.g. `npm i -D @types/lodash`
I find you generally want the @types/ dependencies in the same package.json array as the related dependency (ie, if lodash is a devDependency then `npm i -D` away, but if it is a proper dependency it generally makes sense to `npm i @types/lodash`). Different project's mileage will of course vary, but it makes it easier for downstream dependencies.
As to the remaining cases, a custom definitions file is unfortunately required to avoid compile-time errors.
If finding types is especially difficult, it may depend on a particular library (poorly documented types) and on the tooling you use (e.g., VS Code allows to quickly jump to definition/implementation/etc. on any imported class or component in your code, which often takes you to the place in that library where you can find out which types you need and where to import them from; it was a bigger pain in Vim).

By the way, keep in mind that it is often unnecessary to specify types explicitly because TS can infer them from context. I used to specify way too many types when I was initially learning TS.

I use typescript daily with libraries like those you mentioned. Typescript is not too much overhead, its a huge improvement in the overall developer experience.

The main issue I've had with these libraries and typescript is compiler/IDE performance, which 3.9 seems to suggest improvements.

I'm not quiet sure what issues you are facing without some examples. Given you are listing react libraries, the react community discord server might be a good place to ask specific questions. They have library and typescript specific channels to ask beginner questions in:

https://www.reactiflux.com/

Thanks for your replies. Glad to hear it's working well for you. I want to learn it myself. Was hesitant to ask questions here because this is not Stackoverflow :-) I will check out the discord.

Just to follow up what I meant i created a quick gist. Not working code but maybe can explain it a bit better.

What I mean as an example is this. I create a function that uses styled-components. I have no idea what the return type should be at first. I must dig into internals of styled-components and eventually find something like "AnyStyledComponent" which is basically the same as "Any" but for styled-components.

https://gist.github.com/slackday/7142251cf2bb3660f8c24492aa1...

This is just one example. Just curious. Not to pick at either Typescript or styled-components which are both great!

Not sure if there’s an @types package for this.

If there’s not I recommend disabling your linter for the line and adding an entry in .d.ts. You lose effectiveness but it still works.

My two cents: Learn to write Typescript, not Javascript.

There are Javascript patterns that just don't type well. There are other ways to write your code that will be easier to fit into the type system. It might be more verbose and less reusable at times, but it ends up being a worthwhile tradeoff.

That's general advise and might not apply to the libraries you mention, but it's worked well for us to explicitly call out the differences.

> Any pro typescripter have any tips for how best use it in existing Node/React codebase?

https://react-typescript-cheatsheet.netlify.app is a super detailed guide to exactly this, complete with worked examples for lots of common cases.

Styled Components has a specific section in the docs on using SC with TypeScript, which might be useful too: https://styled-components.com/docs/api#typescript

Yes, my experience was similar (note, last time I worked with Typescript was about a year ago). It was difficult to use libraries with higher order components or just abstractions in general: redux/thunks, formik, materialui, ... I didn't want to use `any` if at all possible, but instead wanted to have properly defined types.

For example, using thunks, one can pass actions directly, or functions that resolve to an action. So, when you're chaining them, it becomes fiddly with types. Compiler complaining about some type or just resolving to any type. I've spent quite some time on that one. Similarly with Formik, I wanted to have generic validators and generic way of applying those validators ... another one that cost me hours to get through and I wasn't satisfied with the result.

There is a lot of information around about Typescript, also various guides like the one here: https://github.com/piotrwitek/react-redux-typescript-guide

But in the end I've felt that while I've become somewhat proficient with Typescript, I was still quite slow to pump out screens and features. And honestly, I didn't enjoy working with it.

(Then there is another can of worms, which is dpendencies and their typings, where it happened that some minor version was bumped and it broke the typings of another library that depended on previous one. So, you might ask, why don't you just pin the dependencies that work together? Well, sometimes an important bug is fixed in a library and you want to upgrade, but you can't, because a person unrelated to library maintainer that handles typings hasn't upgraded them yet ...)

Note that our new official Redux Toolkit package [0] is written in TS and specifically designed to minimize the amount of types you have to declare in your code, and we have TS-specific guide pages in each of the Redux library docs sites [1] [2] [3], and

[0] https://redux-toolkit.js.org

[1] https://redux.js.org/recipes/usage-with-typescript

[2] https://react-redux.js.org/using-react-redux/static-typing

[3] https://redux-toolkit.js.org/usage/usage-with-typescript

> It was difficult to use libraries with higher order components or just abstractions in general

It's probably a good idea to learn how to use generics (type parameters) effectively. For me, it helped immensely to think of them as basically declarative functions in the type system, with the generic/type parameter being just like a function parameter. The cool thing is that while you have to declare them on a function, a lot of times they can be inferred at the call site. With higher level abstractions, most of the time the types just flow through these generics and you get what you want at the other side just based on inference.

>A lot of time is spent figuring out what ”types” to return.

Typescript infers return types, so even if you don't write out a return type, it will still be fully typed.

If you still want to specify the return type, you can take advantage of IDE support. Write the function without an explicit return type, then you can hover over it to see the inferred type, then copy the type.

In my preferred IDE (PyCharm), it's even easier since there's a lightbulb action for it. Alt+Enter > "Specify return type explicitly" > Enter

> I figure there must be something wrong in my approach or in my ts config (no explicit any).

This is a heretic/unpopular opinion, but I think especially for existing JS/React projects, the laxest possible TS setting is the way to go.

You should be able to get to no type errors in your existing JS project with not too many changes. In these contexts (existing projects) I like to think of TS as more editor experience support than seperate language. Modern React is very friendly to TS automatic type inference, so IMHO you should be getting decent benefits from typescript without annotating anything at all. And instead of fixing complicated type errors for no real gain, just // @ts-ignore and move on.

I don't know what your specific issue is with styled-components. Should work pretty much out of the box? My suggestion would be to use CSS objects over string literals because that way you get typechecking for your CSS.

IMO this is a very pragmatic approach to converting a large project from JS to TS. It's the approach I've been using to gradually convert large existing codebases over to TS without committing to a huge up-front refactor.

Definitely agree with you about the IDE experience, just that alone is enough for me to justify the transition.

> This is a heretic/unpopular opinion, but I think especially for existing JS/React projects, the laxest possible TS setting is the way to go.

Not sure why you think it's "heretic/unpopular", but I think it's the only reasonable way to incrementally migrate an existing codebase to TypeScript.

i‘ve read things to the effect of "if you don‘t use noImplicitAny and strictNullChecks you don‘t understand typescript" a bunch but I‘m glad to see the other approach is popular as well.
I used this approach in a project I migrated to TypeScript as well. My strategy was to start loose, fix those warnings, and then gently turn up the rigor when I had the energy to fix the resultant warnings.
.
That is a management problem, not a development one.

The company must pay someone to do the cleanup. Otherwise, of course it won’t work.

Yes, that’s one of my major gripes of Typescript. Once you use something like Emotion you spend a lot of time deciphering the compiler errors. I am told it will be better in the new version. Let’s see if I can find an example
Could you post some snippets with which you have difficulties?