-
-
Notifications
You must be signed in to change notification settings - Fork 109
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
typescript: add base model typing #487
typescript: add base model typing #487
Conversation
I have intentionally left out some methods and fields from the BaseModel class but if I am mistaken and those should be publicly visible, please let me know! (Or if other should be hidden)
I'm probably forgetting a few... |
This is awesome! Thank you for your contribution. It seems that there is a extra brace at the last line of the index.ts file and it makes the CI build fail. |
@J3m5 ah thank you! That's what I get for making my playground in |
…ue/composition-api SetupContext
There are a lot of Typescript errors that came up in the CI build, can you take a look at it ? |
@J3m5 yeah they definitely are. I think stricter null checking is good but enabling that is outside the scope of this PR so I'll revert that. |
This reverts commit 99ae8a2.
So the next issue I want some opinions on is whether or not Model data should be readonly. This will a decent amount of test updates which I'm willing to do, but don't want to do if we decide to not make it readonly :-) I hope the example below clarifies what I'm asking. interface NoteRecord {
id: number
text: string
// other props
}
class Note extends castBaseModel<NoteRecord>() {
static modelName = 'Note'
}
const n = new Note()
n.text = 'this throws an error' // <-- TS error: NoteRecord props are readonly
const nClone = n.clone()
nClone.text = 'this is fine!' // NoteRecord props are not readonly in clone Pros of making it readonly:
Cons:
Thoughts? |
Will this only impact typescript users ? |
Correct, only TS users will be affected by this. You can always cast away the readonly if you want const n = new Note()
n.text = 'this throws an error' // <-- TS error: NoteRecord props are readonly
const casted = n as ModelClone<NoteRecord>
casted.text = 'this is fine' // no error I will have to think about whether we could make this something the user can augment so they could globally disable it if they don't want it and don't want to cast everywhere. |
If this can be globally disabled, I think we can enforce it by default and add some info in the docs. |
There is now an exported interface declare module 'feathers-vuex' {
interface FeathersVuexTypeOptions {
'model-readonly': false
}
} if |
It looks good ! It seems that some tests like this one needs to be changed in order to follow the readonly constraint of the modelName property on the BaseModel. And here the FeathersVuexGlobalModels need to be augmented, like mentionned here, if I understand correctly. And I've noticed that the globalModels object is casted as FeathersVuexGlobalModels in some place but not in others, is this intended ? |
This is ready to merge pending feedback on |
I think we should let them as they are, at least for now. What do you think @marshallswain ? |
If static props are still mutable, then I'll go ahead and merge this. |
@marshallswain I've made all static props mutable except for |
This would address #519 |
Brought up to date with latest master. I believe this is ready to merge if there is still interest. |
Merged! Thanks @hamiltoes! This is a huge update. Thanks for your input, as always, @J3m5! |
Summary
Add explicit typing to BaseModel to aide development.
Currently, the BaseModel has type
any
which makes intellisense nearly useless. I am adding interfaces with jsdoc strings to further improve the already great experience of developing with FeathersVuex.no
no
Other Information
Because the BaseModel class is constructed inside a function, it's type cannot be used outside of that function. My solution is to define multiple interfaces: one for the static side
ModelStatic
, and one for the instance sideModelInstance
. Both of these interfaces are generic and take a type parameter that describes the service's data structure. There is also aModelInstanceClone
interface for clones.Ideally, we would just make the BaseModel returned from the FeathersVuex setup function generic, but I could not find a way to cast the non-generic BaseModel of type
any
to a generic interface. So my solution is to return BaseModel casted to aModelStatic<{}>
and an additional methodcastBaseModel
from the setup function which can be used to add typing for each service's data interface.Notes
Model<D>
is of typeModelInstance<D> & Readonly<D>
ModelClone<D>
is of typeModelInstanceClone<D> & D
commit()
andreset()
methods inModelInstanceClone
since they just error on non-clones.Examples
If you want to check out how this works with intellisense, take a look at the following. Or even better, clone this branch, paste it in
index.ts
and play with it :-)Screenshots
Autocomplete for
OldFashionedNote
instancecommit()
orreset()
Autocomplete for
OldFashionedNote
classAutocomplete for
FancyTypedNote
instanceNoteRecord
fields!Augmentable interfaces
Users can also augment interfaces defined for
feathers-vuex
to add more helpful typingAnd then they can get autocomplete on
Model.models
,Model.store
,Vue.$FeathersVuex
, setupInstance'sstore
andmodels
params, etc.