Hello,
I have used feathers-vuex with TypeScript in a project and I made a set of helpers that I think could be really useful to other developers, here you can see the source code and below is an example of usage:
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
import { namespace, Action } from 'vuex-class'
import { ActionGet, ActionCreate, ActionPatch } from '../types/feathers'
import { Person } from '../types/models'
const PeopleAction = namespace('people', Action)
@Component
export default class CreateEditPeople extends Vue {
@PeopleAction('get') getPerson: ActionGet<Person>
@PeopleAction('create') createPerson: ActionCreate<Person>
@PeopleAction('patch') patchPerson: ActionPatch<Person>
// some extra here
}
TypeScript reads it really well and all suggestions and autocomplete helps a lot.
Cool!
@nickmessing how would one use your feathers-vuex-helpers.ts file in their project? This looks like something that ought to go into the docs in a "Using with TypeScript" section. We can link to your helpers gist.
@marshallswain, I could finalize the typings and make a PR to make them universal so people can use them with bot class-component definition and classic component definition if you want to support that.
I know absolutely nothing about TypeScript, but I know that a lot of people
will appreciate this. I might even learn something, here, so I’d like to
support it, yeah. :) please make a PR when you have time. That would be
terrific!
On Sun, Apr 22, 2018 at 3:25 AM Nick Messing notifications@github.com
wrote:
@marshallswain https://github.com/marshallswain, I could finalize the
typings and make a PR to make them universal so people can use them with
bot class-component definition
https://gist.github.com/nickmessing/f206056cb0eb8879b068f727a1aaecbb
and classic component definition
https://gist.github.com/nickmessing/dfc8264ecc17d68ed5aaa82bd253d4e5 if
you want to support that.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/feathers-plus/feathers-vuex/issues/114#issuecomment-383367639,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAH3WTT3-Bo75lv1FXsC0p0WseLN8_0xks5trEx5gaJpZM4TcmIA
.
@rayfoss is your 👎 reaction a reflection of your dislike for TypeScript? Or is it about the quality of the recommendation?
@marshallswain Features should have a good reason to exist because they take time to add and maintain. My vote is just a reflection of this feature being 99% unlikely to be of use to me. I'm waiting for native types to come to Node. Fun fact, I used to use Typescript in Angular 2.
I think TS support is good for the project, as long as it doesn't get in the way for everybody else with a ton of irrelevant files and dependencies everywhere. Forcing it is definitely a bad idea, as there are many many hold outs and designers using js or flow. Remember coffeescript?
@rayfoss Thanks for the update. I have similar sentiment. If @nickmessing's suggestions work without requiring modifying the repo, that's the perfect solution, in my opinion.
I've added a link to this issue in the docs. I'm closing this issue, now. Thanks, guys.
Docs link is here: https://feathers-plus.github.io/v1/feathers-vuex/common-patterns.html#Working-with-TypeScript
I have some typings for feathers-vuex too, I'm planning on putting together a PR at some point, but for now here they are.
Updated 7/30/19
import { Application, Query, Params } from "@feathersjs/feathers";
import { StoreOptions, Plugin, Commit, Dispatch, Store } from "vuex";
import Vue, { PluginObject, ComponentOptions } from "vue";
import { Request } from "request";
import { PropsDefinition } from "vue/types/options";
/**
* default export
*/
declare const FeathersVuexDefault: (client: Application, options: Partial<FeathersVuexOptions>) => FeathersVuexResult;
export default FeathersVuexDefault;
interface FeathersVuexResult {
service<ModelType = any, IDType = string>(servicePath: string, options?: Partial<FeathersVuexOptions>): Plugin<FeathersVuexServiceState<ModelType, IDType>>;
auth<UserType>(options?: FeathersVuexAuthOptions): Plugin<FeathersVuexAuthState<UserType>>;
FeathersVuex: PluginObject<any>;
}
interface FeathersVuexCoreProps {
/**
* @default 'id'
*/
idField: string;
/**
* @default false
*/
autoRemove: boolean;
/**
* @default true
*/
enableEvents: boolean;
/**
* @default false
*/
addOnUpsert: boolean;
/**
* @default false
*/
diffOnPatch: boolean;
/**
* @default false
*/
skipRequestIfExists: boolean;
/**
* @default false
*/
preferUpdate: boolean;
/**
* @default false
*/
replaceItems: boolean;
}
interface FeathersVuexStoreOptions<Store> extends Pick<StoreOptions<Store>, 'state' | 'getters' | 'mutations' | 'actions'> { }
interface FeathersVuexOptions<Store = any> extends FeathersVuexStoreOptions<Store>, FeathersVuexCoreProps {
/**
* @default 'path'
*/
nameStyle?: 'short' | 'path';
namespace: string;
apiPrefix: string
modelName: string;
instanceDefaults?: any;
/**
* @debug false
*/
debug: boolean;
}
/**
* Service Model
*/
// This is an interface because it needs to return FeathersVuexModelClass & T
// from the constructor, which isn't allowed with classes
interface FeathersVuexModelClass<ModelType = any> {
namespace: string;
className: string;
modelName: string;
store: Store<any>; // todo: Type the VuexStore so we can include it here
options: FeathersVuexOptions;
new(data?: Partial<ModelType>, options?: FeathersVuexModelOptions): FeathersVuexModel<ModelType>;
create: (params?: GetParams) => Promise<FeathersVuexModel<ModelType>>;
save: (params?: GetParams) => Promise<FeathersVuexModel<ModelType>>;
patch: (params?: GetParams) => Promise<FeathersVuexModel<ModelType>>;
update: (params?: GetParams) => Promise<FeathersVuexModel<ModelType>>;
clone: () => Promise<FeathersVuexModel<ModelType>>;
reset: () => Promise<FeathersVuexModel<ModelType>>;
remove: (params?: GetParams) => Promise<FeathersVuexModel<ModelType>>;
}
export type FeathersVuexModel<T> = FeathersVuexModelClass<T> & T;
type GetParams = Exclude<Params, 'paginate'>;
type FindParams = Params;
interface FeathersVuexModelOptions {
isClone?: boolean;
skipCommit?: boolean;
}
interface ModelStatic<T> {
new(data?: Partial<T>, options?: FeathersVuexModelOptions): FeathersVuexModel<T>;
find: (params?: FindParams) => FeathersVuexModel<T>[];
findInStore: (params?: FindParams) => FeathersVuexModel<T>[];
get: (id: any, params?: GetParams) => FeathersVuexModel<T>[];
getFromStore: (id: any, params?: GetParams) => FeathersVuexModel<T>[];
}
/**
* Vue Plugin
*/
type FeathersVuexGlobalModelsIndex<T> = {
[P in keyof T]: {
new(data?: Partial<T[P]>, options?: FeathersVuexModelOptions): FeathersVuexModel<T[P]>;
find: (params?: FindParams) => FeathersVuexModel<T[P]>[];
findInStore: (params?: FindParams) => FeathersVuexModel<T[P]>[];
get: (id: any, params?: GetParams) => FeathersVuexModel<T[P]>[];
getFromStore: (id: any, params?: GetParams) => FeathersVuexModel<T[P]>[];
}
}
// This doesn't work because merging this into a string signature isn't a valid type
// Leaving it here so that hopefully someone can figure it out in the future
// type FeathersVuexGlobalModelsByPath<T> = {
// byServicePath: FeathersVuexGlobalModelsIndex<T>
// }
/**
* @description The type for the $FeathersVuex vue plugin. To type this stronger include the
* following in a typings.d.ts file
*
*
* @example
* declare module "vue/types/vue" {
* import { FeathersVuexGlobalModels } from "feathers-vuex";
* interface Vue {
* $FeathersVuex: FeathersVuexGlobalModels<Services>;
* }
* }
*
* @description Where services is something like this
* @example
* interface Services {
* users: User
* //...
* }
*
* interface User {
* name: string;
* rating: number;
* //...
* }
*
*/
export type FeathersVuexGlobalModels<T = any, P = any> = FeathersVuexGlobalModelsIndex<T> // & FeathersVuexGlobalModelsByPath<P>; // See comment above
/**
* Add FeathersVuex to the Vue prototype
*/
declare module "vue/types/vue" {
interface FeathersVuexPluginType extends FeathersVuexGlobalModels { }
interface Vue {
$FeathersVuex: FeathersVuexPluginType;
}
}
/**
* Auth module factory
*/
interface FeathersVuexAuthOptions<State = FeathersVuexAuthState> extends FeathersVuexStoreOptions<State> {
userService: string
}
interface FeathersVuexAuthState<UserType = any> {
accessToken?: string, // The JWT
payload: Object, // The JWT payload
isAuthenticatePending: boolean,
isLogoutPending: boolean,
errorOnAuthenticate: Error | undefined,
errorOnLogout: Error | undefined,
user: UserType
}
interface FeathersVuexServiceState<ModelType = any, IDType = string> extends FeathersVuexCoreProps {
ids: IDType[],
keyedById: {
[i: string]: ModelType
}, // A hash map, keyed by id of each item
currentId?: IDType, // The id of the item marked as current
copy?: ModelType, // A deep copy of the current item
servicePath: string // The full service path
paginate: boolean, // Indicates if pagination is enabled on the Feathers service.
pagination: { [key: string]: ModelType[] }, // Indicates if pagination is enabled on the Feathers service.
isFindPending: boolean,
isGetPending: boolean,
isCreatePending: boolean,
isUpdatePending: boolean,
isPatchPending: boolean,
isRemovePending: boolean,
errorOnfind: Error | undefined,
errorOnGet: Error | undefined,
errorOnCreate: Error | undefined,
errorOnUpdate: Error | undefined,
errorOnPatch: Error | undefined,
errorOnRemove: Error | undefined
}
/**
* Data Components
*/
// todo: use generic FeathersVuex*Computed
export const FeathersVuexFind: ComponentOptions<
Vue,
FeathersVuexFindData,
FeathersVuexFindMethods,
FeathersVuexFindComputed,
PropsDefinition<FeathersVuexFindProps>,
FeathersVuexFindProps>;
export const FeathersVuexGet: ComponentOptions<
Vue,
FeathersVuexGetData,
FeathersVuexGetMethods,
FeathersVuexGetComputed,
PropsDefinition<FeathersVuexGetProps>,
FeathersVuexGetProps>;
interface FeathersVuexFindData {
isFindPending: boolean;
}
interface FeathersVuexGetMethods {
findData(): Promise<void>;
fetchData(): Promise<void>;
}
interface FeathersVuexFindComputed<T = any> {
items: T[];
scope: { [key: string]: any }
pagination: { [key: string]: any }
}
interface FeathersVuexDataComponentProps {
service: string;
query?: any;
queryWhen?: boolean | ((id: string, ...args: any[]) => boolean);
fetchQuery: any;
watch?: string | string[];
local?: boolean;
editScope?: (params: FeathersVuexEditScopeArgs) => { [key: string]: any };
}
interface FeathersVuexEditScopeArgs {
item: any;
isGetPending: boolean
}
interface FeathersVuexFindProps extends FeathersVuexDataComponentProps {
qid?: string;
}
interface FeathersVuexGetData {
isFindPending: boolean;
isGetPending: boolean;
}
interface FeathersVuexFindMethods {
getArgs(queryToUse?: Query): any[];
getData(): Promise<void>;
fetchData(): Promise<void>;
}
interface FeathersVuexGetComputed<T = any> {
item: T | null;
scope: { [key: string]: any }
}
interface FeathersVuexGetProps extends FeathersVuexDataComponentProps {
id?: number | string;
}
/**
* Utilities
*/
// todo: these mixins could probably have much better return types to make use of the generic
export function makeFindMixin<T = any>(options: MakeFindMixinOptions<T>): (ComponentOptions<Vue> | typeof Vue);
export function makeGetMixin<T = any>(options: MakeGetMixinOptions<T>): (ComponentOptions<Vue> | typeof Vue);
interface DataComponentOptions {
/**
* Service name, either service or name are required
*
* note: it looks like this can also be a function, but I don't quite understand the effects
* so I'm not documenting it right now
*/
service: string;
/**
* Service name, either service or name are required
*/
name?: string;
params?: Params;
/**
* @default () => true
*/
queryWhen?: boolean | ((params: Params) => boolean);
/**
* @default false
*/
local?: boolean;
qid?: string | number | symbol;
debug?: boolean;
watch: string | boolean | string[];
}
interface MakeFindMixinOptions<T = any> extends DataComponentOptions {
fetchQuery?: Query;
items?: T[];
}
interface MakeGetMixinOptions<T = any> extends DataComponentOptions {
fetchParams?: Params;
id?: any;
item?: T;
}
/**
* Init Auth
*/
interface InitAuthOptions {
commit: Commit;
dispatch: Dispatch;
req: Request;
cookieName: string;
moduleName: string;
feathersClient: Application;
}
export function initAuth(options: InitAuthOptions): Promise<object>;
@NickBolles I just removed my client side project that used TypeScript, just to regenerate it as a JavaScript project.
Steps I tried to add typings to feathers-vuex:
Finally, I did what I said in the beginning and removed TypeScript just so I can follow along with a tutorial using this library.
@bartduisters There are probably other ways to achieve this without built in typings (which I believe are coming in v2), but here's how I have it setup
I have a folder @types/feathers-vuex with an index.d.ts in it with that content from my post above (which I just updated). Then I have a file @types/feathers-vuex-app.d.ts with the following content in it.
import { Services } from "../store";
import { FeathersVuexGlobalModels } from "feathers-vuex";
declare module "vue/types/vue" {
interface FeathersVuexPluginType extends FeathersVuexGlobalModels<Services> { }
}
This will extend the Vue prototype typings with
This file is picked up by typescript by declaring it in the "typeRoots" in the tsconfig.json.
{
"compilerOptions": {
"typeRoots": [
"./node_modules/@types/",
"./client/@types/"
],
}
}
where store/index.ts contains this (and more). You should probably break this into it's own file actually, but I have it in store/index.ts along with my store content. Also this is importing the user service from my feathers server, which was generated by feathers-plus cli. It could simply be an interface that you define though.
import { User } from "../../server/services/v1/users/users.interface";
export interface Services {
users: User;
}
Thanks for the clarification. Will try it as soon as I try another project with TypeScript and feathers-vuex!
Most helpful comment
I have some typings for feathers-vuex too, I'm planning on putting together a PR at some point, but for now here they are.
Updated 7/30/19