tanepiper/svelte-formula

A better choice of typing for form value

sserdyuk opened this issue · 17 comments

Hi @tanepiper

I'd like to make a case for a different choice of generic's type signature. The current choice of Record<string, unknown> type doesn't take advantage of Typescript type checking at compile time, which opens a door for hard to troubleshoot runtime issues that otherwise could be prevented. For example, this type allows me to write $validity?.typoInTheFieldName?.valid checking in non-existent field's validity.

I'd like to propose to change the generic's signature to T extends {} = Record<string, unknown>. The default value provides backward compatibility and easiness of use for people who don't care about typing, but gives us an opportunity to provide a strong type definition that takes full advantage of compile time checks.

Please see my quick illustration the difference it makes: https://stackblitz.com/edit/typescript-kxf2jj?file=index.ts

@sserdyuk Thanks I'll have a look into this, most likely this one will be a few days. I'm not happy with the typings just now, either.

@sserdyuk I've added a PR with the changes - also took some time to do a little internal refactoring and removed some internal types as they are no longer needed. I'll release it later today

Hey @sserdyuk - so running linting and it really doesn't like this.

Screenshot 2021-03-15 at 20 36 06

Looking around this is not recommended:

@sserdyuk OK found what seems like a workaorund which is to create a Class then set it

This seems to work: https://stackblitz.com/edit/typescript-zvm6we?file=index.ts

Otherwise, it's the following errors:

Screenshot 2021-03-15 at 21 38 28
Screenshot 2021-03-15 at 21 38 06

OK! So a bit of digging came to this conclusion microsoft/TypeScript#15300

tl;dr - Cannot use Record<string, unknown> type as this forces your type to need an index signature - however Record<string, any> works fine.

It looks like most of the ESLint rules are footgun preventers, but I think this along with the stores being { [K in keyof T]: T[K] } should give pretty strong typed output for you

This gets too complicated. The { [K in keyof T]: T[K] } is basically T. So maybe it is simply Test<T = Record<string, unknown>> what we need? https://stackblitz.com/edit/typescript-achsoc?file=index.ts

After thinking a bit more, simple T would accept a number or a string where we really need an object. I am going to think about this a bit more.

What about Test<T extends object = Record<string, unknown>> https://stackblitz.com/edit/typescript-1fjspb?file=index.ts
I've tried to run the lint before suggesting it here but it appears that simply npm i isn't enough to configure the project.

Hello @sserdyuk I haven't forgotten about this. As I want to make the API more stable I want this to be as much as a final change on this as it can be, so get it right the first time. Doing T extends object causes the same error - it's a very strict ban-types rule in ESLint.

The options are:

  • Go with excluding the rule in ESLint
  • Simplify type layer - actually inner application doesn't need to know T from userland, so just use T at the constructor layer and enrich the store export type only with { [K in keyof T]: T[K] }
  • Use Record<string, any>

No worries. I've put together a panel of tests https://stackblitz.com/edit/typescript-3s5qye?file=index.ts where you can switch the type definitions at the top and see how they work. Based on that, I think that <T extends Record<string, any>> is perfectly fine if it makes eslint happy.

@tanepiper Just checking if I can do anything else to help you finish this one.

@sserdyuk hey - so at this point yes help would be appreciated.

I'd be happy to read and approve any PR (and please add yourself as a contributor)

@tanepiper I'd be glad to but the monorepo confuses me. Would you publish contribution guidelines like how to set things up and what to test before submitting a PR?

The repo uses Yarn instead of NPM because it needs to use its resolver, the other tool is nx.

Once installed there's a couple of commands:

npm run start formula-app --rollupConfig=apps/formula-app/rollup.config.js - this starts the small testing app I use running, it's not very pretty but it lets me test thing (I already have a branch investigating using this app for Cypress e2e testing as well but there is an issue with the internal lib resolving)

npm run build svelte-formula --skip-nx-cache --watch If you run that with the above, you get hot-reloading when you change the lib`

npm run start docs-site Runs the docosaurus site under packages/doc-site

npm run nx test svelte-formula - Runs the unit test suite

I'd also have to do the NPM publish, I don't have it automated for now - I have scripts in another repo but just haven't had time to configure it.

@sserdyuk Hey just wanted to check in to see if you did anything? If not then I'll be up for looking at it over the next couple of days and releasing an update. Let me know so we avoid repeating work