ktsn / vuex-class

Binding helpers for Vuex and vue-class-component
MIT License
1.73k stars 86 forks source link

[feature request] Would it be possible to have the right property-name inferred if its omitted? #1

Closed Evertt closed 7 years ago

Evertt commented 7 years ago

I really know next to nothing about typescript and type annotations, but it would be nice if it would be possible to do this:

import Vue from 'vue'
import Component from 'vue-class-component'
import {
  State,
  Getter,
  Action,
  Mutation,
  namespace
} from 'vuex-class'

const ModuleGetter = namespace('path/to/module', Getter)

@Component
export class MyComp extends Vue {
  @State foo
  @Getter bar
  @Action baz
  @Mutation qux
  @ModuleGetter quz

  created () {
    this.foo // -> store.state.foo
    this.bar // -> store.getters.bar
    this.baz({ value: true }) // -> store.dispatch('baz', { value: true })
    this.qux({ value: true }) // -> store.commit('qux', { value: true })
    this.quz // -> store.getters['path/to/module/quz']
  }
}

And I mean optionally. So you can still supply some string to specify which property-name or which action-name you're referring to, but if you choose to not supply any then it will be inferred from the variable name.

Is that possible? And if so, would you be willing to make it?

ktsn commented 7 years ago

Oh, it's really nice :) I'll try to implement that.

ktsn commented 7 years ago

Shipped in v0.1.3

Evertt commented 7 years ago

Awesome man, thanks!

I have a question, since I know so little about typescript. Could you explain to me how you made it possible for the empty parentheses to also be omitted? I asked the guy of this repository if he's willing to do the same and he's not, so I would like to fork his repo and make it myself. But I have no idea how that works.

Evertt commented 7 years ago

@ktsn ^ ?

ktsn commented 7 years ago

To understand how to implement that behavior, you should know what actually decorator is.

A property decorator is just a function that receives prototype object of the class and decorated key. So if we write @Foo for property, Foo must belong to PropertyDecorator type.

declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void
const Foo: PropertyDecorator

On the other hand, if we write @Foo('bar'), Foo must be a function that returns decorator. So the Foo's declaration would be like following:

const Foo: (bar: string) => PropertyDecorator

Here, we would like to support both syntax of @Foo and @Foo('bar'), so we need to overload the function so the type looks like:

function Foo (target: Object, propertyKey: string | symbol): void
function Foo (bar: string): (target: Object, propertyKey: string | symbol) => void

For implementation, we can detect which syntax the user is using by checking argument type. In this case, if @Foo('bar') syntax is used, the first argument must be string.

function Foo (targetOrBar: Object | string, key?: string | symbol): any {
  if (typeof targetOrBar === 'string') {
    // @Foo('bar')
    return // ...
  }
  // @Foo
  return // ...
}

This is all about the trick of this decorator's behavior. I'm using this trick in these line. https://github.com/ktsn/vuex-class/blob/master/src/bindings.ts#L78-L90

Evertt commented 7 years ago

Wow, thank you for that comprehensive response. I trust that I can make it work with that information (and maybe some more googeling if I get stuck).