Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] TypeScript rewrite of a few Final Form packages #486

Open
Deckstar opened this issue Oct 8, 2023 · 1 comment
Open

[RFC] TypeScript rewrite of a few Final Form packages #486

Deckstar opened this issue Oct 8, 2023 · 1 comment

Comments

@Deckstar
Copy link

Deckstar commented Oct 8, 2023

Hello! 🙂

I've rewritten a few Final Form packages in TypeScript, and I thought I'd share the results here.

This post is quite literally a request for comments 🙂

I'm writing this issue because I've done quite a bit of work over the past few months on this rewrite, so obviously, I want other other people to have the chance to enjoy the fruits of my labor and not just me 😄 I was also wondering if my project could be useful for the future of the Final Form community as a whole. Perhaps it could be merged into the original libraries at some point? I would like it to be! So I'm very open to any feedback that anyone might have about my rewrites, if you have any.

You can see my forked code here, at my final-form-ts repo.

You can also try out any of the TS packages by downloading them from npm, e.g. "@deckstar/final-form".


I originally posted about my rewrite project on the Final Form Discord channel a few months ago. Then, a few days ago, I noticed that there have been some calls for rewriting the library in TypeScript (e.g. #448, or for React Final Form #1028), so I thought I'd share my work here. Thus this RFC was born!

Summary of changes

The libraries I refactored were:

  1. final-form
  2. final-form-arrays
  3. final-form-focus
  4. react-final-form
  5. react-final-form-arrays

Because these are the ones I've been using in production.

While rewriting the packages, I ended up making quite a few changes to the project's structure and typings. However, the run-time code changes have been very minimal (almost none). Off the top of my head, these are the main changes that I introduced:

Library changes

  1. I've merged the code from 5 libraries into a monorepo (git commits included). This is a huge change, and because of this I don't expect my work to be merged to the real library anytime soon, if ever.
    • I liked the monorepo structure because it allowed me to easily import types between packages while developing, and to see all my work in one place. While refactoring, I often had to jump back and forth between many packages to keep the types consistent. Having a monorepo setup just seemed like the easiest way to handle this.
  2. Regarding versioning, I've restarted all versions at 1.0.0. So I haven't followed the old Final Form versioning, where the latest FF is 4.20.9, for example.
  3. Also, in my npm packages, I've currently set all the packages to use strictly matching versions as peer dependencies (e.g. @deckstar/react-final-form@1.0.9 has a peer dependency of @deckstar/final-form@1.0.9 and no other version). This ensured that typings always match between packages. However, it also meant that package versions had to always be updated for all packages, even if some of them didn't actually have any changes.
    • I actually don't like this part of the setup very much and would be open to discussing better alternatives 😄

Run-time changes

Almost none, except:

  1. I added a status state, a setStatus action and an initialStatus config prop.
    • I stole these from Formik's status API. I was rewriting a project from Formik to Final Form for performance reasons, and because Formik had been dead for 2 years.

Typing changes

Here is where the bulk of the work was done.

  1. One feature that I'm quite happy about, is that I added JSDoc comments to a lot of the user-facing types. So now, if you hover over things like values, active or initialValues, then you should get the same documentation as from the Final Form website right in your IDE.

    • I think this is a very helpful change that saves me a lot of time alt-tabbing and searching through the Final Form site — especially when working with packages like React Final Form, which forced me to alternate between documentation on two websites.
  2. Most of the typing work was done with rewriting the generic arguments for types:

    1. I've made Subscription a generic parameter:
      • In Final Form, a subscribed-to value is always returned, while an unsubscribed to value is always undefined. However, the type is always {{type}} | undefined, which is misleading. It's also dangerous: people can mark variables as non-nullable (e.g. to avoid checking for undefined at runtime), and then forget to handle the case if the subscription changes.
      • With a generic subscription, I could make it so that only keys that are unsubscribed to have the | undefined added on to the type.
    2. I've removed InitialValues as a generic. Personally, I never found this very useful and I think it just slowed down development without providing much type safety. Instead, I hardcoded initialValues to just be Partial<FormValues>. I figured that if anyone needs something else, they can just cast the type later (e.g. const initialValues = form.initialValues as MyInitialValues).
    3. I've made more helpful types for Mutators, including BoundMutator types, to help handle the types of functions before and after they were bound to the form API.
    4. FieldConfig can now take in FormValues as an optional second generic parameter. This helps with props like getValidator, which allow access to form values.
    5. Some generic argument positions were moved around for convenience.
      • For example, in React Final Form, I found that I didn't need the InputValue generic parameter as often as the FormValues, and that using the InputValue = FieldValue default case was good enough for most situations. So I moved FormValues ahead of InputValue.
    6. And probably more random changes that I've forgotten about 😄

I realize that a many of these changes wouldn't play very well with the existing libraries (especially the monorepo refactor and reinitialized version numbers). Please understand that I was initially planning on using this TS version in a single private project only.

It was only when I realized how satisfied I was with it that I thought that other people might like it too. At the very least, I know I'd personally love to use it at work. But obviously my team probably wouldn't take too kindly to me promoting my pet project over an established library, and for good reason 😄


If any maintainers have time to have a look, please let me know what you think about any of this 🙂

PS: Also, please note that a few tests are currently broken: some of them fail because they fail to import from other packages in the monorepo (Jest is throwing Cannot find module errors). I've tried a lot of fixes to no avail. If anyone could help me fix that, that would be awesome 😄 I can confirm that they do pass if the imports are resolved, though. (never mind, solved it 😄)

@rosskevin
Copy link
Contributor

These all look like sane decisions and an evolution of the project. Regarding monorepo, it may have originally been split individually for scale/framework/scope creep/maintenance concerns. So, e.g. adding a vue based project might not be something you want to do in a monorepo (mixing with react and others). This is my only reservation with your structure. So my recommendation might be a final-form monorepo, a final-form-react monorepo etc.

My worry about this repo is that it might go unmaintained, and we use it quite heavily. In fact I'm updating our libraries and this is on my watchlist to make sure everything is healthy. If this repo becomes unmaintained, we'll keep your repo in mind.

I'm just one user, but I would like to see your work incorporated into the original body of work with you added as a maintainer. @erikras is this idea of interest to you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants