Skip to content

Commit d71891a

Browse files
Merge pull request #487 from hamiltoes/ts/typed-base-models
typescript: add base model typing
2 parents deb8b6e + bd96d6d commit d71891a

File tree

13 files changed

+517
-94
lines changed

13 files changed

+517
-94
lines changed

docs/composition-api.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,21 @@ export default new Router({
779779

780780
Now, the `Post.vue` file only requires to have a `prop` named `id`. Vue Router will pass the params from the route as props to the component. See the [first useGet example](#useget) for a component that would work with the above route. The vue-router documentation has more information about [Passing Props to Route Components](https://router.vuejs.org/guide/essentials/passing-props.html#passing-props-to-route-components)
781781

782+
### Composing with Model types
783+
784+
Both `useGet` and `useFind` have an optional type parameter for the Model type which is used as the type for the returned item(s).
785+
786+
```ts
787+
// Destructure Model class from global models object
788+
const { User } = Vue.$FeathersVuex.api
789+
790+
// use useGet with User Model
791+
useGet<typeof User.prototype>(/* ... */)
792+
793+
// use useFind with User Model
794+
useFind<typeof User.prototype>(/* ... */)
795+
```
796+
782797
## Conventions for Development
783798

784799
### Params are Computed

docs/model-classes.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,77 @@ User.instanceDefaults = function() {
5151
}
5252
```
5353

54+
### BaseModel typing
55+
56+
BaseModel typing gives helpful IDE autocomplete and errors when using Model classes.
57+
58+
Since Feathers-Vuex doesn't know what your data looks like, you will need to help define your underlying model data's interface.
59+
60+
By default, Model classes are `string` indexable with value of type `any`. This isn't super helpful...
61+
62+
```ts
63+
// Just like before, we define our class as an extension of BaseModel
64+
class User extends BaseModel { /* ... */ }
65+
66+
// Augment the User Model interface
67+
interface User {
68+
email: string
69+
password: string
70+
}
71+
```
72+
73+
Now, whenever we access a `User` model, all fields defined in the interface will be available in IDE auto-complete/intellisense along with the model methods/props.
74+
75+
If you already have a User interface defined under a different name, just define a new interface with the same name as your Model class like so
76+
77+
```ts
78+
// if our User interface already exists as UserRecord
79+
interface User extends UserRecord {}
80+
```
81+
82+
To further enhance typing, you can augment FeathersVuex types to aid development in other parts of your app. It's important to note the differences in the following example if you do or do not setup a `serverAlias`.
83+
84+
```ts
85+
// src/store/user.store.ts
86+
import { ServiceState } from 'feathers-vuex'
87+
88+
class User extends BaseModel { /* ... */ }
89+
interface User { /* ... */ }
90+
const servicePath = 'users'
91+
92+
declare module "feathers-vuex" {
93+
interface FeathersVuexStoreState {
94+
[servicePath]: ServiceState<User>
95+
}
96+
97+
// Only if you setup FeathersVuex without a serverAlias!!
98+
interface FeathersVuexGlobalModels {
99+
User: typeof User
100+
}
101+
}
102+
103+
// Only if you setup FeathersVuex with a serverAlias!!
104+
declare module "src/store" {
105+
interface MyApiModels {
106+
User: typeof User
107+
}
108+
}
109+
```
110+
111+
If you have setup a `serverAlias`, you need to add the following to `src/store/index.ts`.
112+
113+
```ts
114+
// src/store/index.ts
115+
export interface MyApiModels { /* Let each service augment this interface */ }
116+
declare module "feathers-vuex" {
117+
interface FeathersVuexGlobalModels {
118+
'my-api-name': MyApiModels
119+
}
120+
}
121+
```
122+
123+
Replace `my-api-name` with the `serverAlias` you used when setting up FeathersVuex.
124+
54125
## Model attributes
55126

56127
The following attributes are available on each model:

src/auth-module/auth-module.state.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ eslint
33
@typescript-eslint/explicit-function-return-type: 0,
44
@typescript-eslint/no-explicit-any: 0
55
*/
6+
import { AuthState } from './types'
7+
68
export default function setupAuthState({
79
userService,
810
serverAlias,
911
responseEntityField = 'user',
1012
entityIdField = 'userId'
1113
}) {
12-
const state = {
14+
const state: AuthState = {
1315
accessToken: null, // The JWT
1416
payload: null, // The JWT payload
1517
entityIdField,
@@ -21,6 +23,7 @@ export default function setupAuthState({
2123
errorOnAuthenticate: null,
2224
errorOnLogout: null,
2325
user: null, // For a reactive user object, use the `user` getter.
26+
userService: null,
2427
serverAlias
2528
}
2629
// If a userService string was passed, add a user attribute

src/auth-module/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ export interface AuthState {
22
accessToken: string
33
payload: {}
44
entityIdField: string
5+
responseEntityField: string
56

67
isAuthenticatePending: boolean
78
isLogoutPending: boolean
89

910
errorOnAuthenticate: Error
1011
errorOnLogout: Error
1112
user: {}
13+
userService: string
14+
serverAlias: string
1215
}

src/index.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,21 @@ import prepareMakeAuthPlugin from './auth-module/make-auth-plugin'
1818
import useFind from './useFind'
1919
import useGet from './useGet'
2020

21-
import { FeathersVuexOptions, HandleEvents } from './service-module/types'
21+
import {
22+
FeathersVuexOptions,
23+
HandleEvents,
24+
Model,
25+
ModelStatic,
26+
ModelSetupContext,
27+
Id,
28+
FeathersVuexStoreState,
29+
FeathersVuexGlobalModels,
30+
GlobalModels,
31+
} from './service-module/types'
2232
import { initAuth, hydrateApi } from './utils'
2333
import { FeathersVuex } from './vue-plugin/vue-plugin'
34+
import { ServiceState } from './service-module/service-module.state'
35+
import { AuthState } from './auth-module/types'
2436
const events = ['created', 'patched', 'updated', 'removed']
2537

2638
const defaults: FeathersVuexOptions = {
@@ -73,7 +85,7 @@ export default function feathersVuex(feathers, options: FeathersVuexOptions) {
7385
BaseModel,
7486
makeAuthPlugin,
7587
FeathersVuex,
76-
models,
88+
models: models as GlobalModels,
7789
clients
7890
}
7991
}
@@ -92,5 +104,13 @@ export {
92104
models,
93105
clients,
94106
useFind,
95-
useGet
107+
useGet,
108+
AuthState,
109+
Id,
110+
Model,
111+
ModelStatic,
112+
ModelSetupContext,
113+
ServiceState,
114+
FeathersVuexGlobalModels,
115+
FeathersVuexStoreState,
96116
}

0 commit comments

Comments
 (0)