programing

클래스 스타일 또는 데코레이터 구문을 사용하지 않고 mapGetters, mapActions Vuex 및 typescript의 인텔리센스를 얻는 방법

javaba 2022. 10. 26. 21:30
반응형

클래스 스타일 또는 데코레이터 구문을 사용하지 않고 mapGetters, mapActions Vuex 및 typescript의 인텔리센스를 얻는 방법

한동안 Vue.js와 Vuex를 사용하지만 항상 javascript와 함께 사용합니다.

좀 더 구체적으로 말하면 Typescript, nuxt.js와 함께 Vue를 사용하려고 하는데 데코레이터나 style-class-component를 사용하지 않고 일반 Vue 구문만 계속합니다.

Vuex 스토어에 있는 코드입니다.

/store/todos/types.ts

export interface Todo {
  id: number
  text: string
  done: boolean
}

export interface TodoState {
  list: Todo[]
}

/store/todos/state.ts

import { TodoState } from './types'

export default (): TodoState => ({
  list: [
    {
      id: 1,
      text: 'first todo',
      done: true
    },
    {
      id: 2,
      text: 'second todo',
      done: false
    }
  ]
})

/store/todos/mutations.ts

import { MutationTree } from 'vuex'
import { TodoState, Todo } from './types'

export default {
  remove(state, { id }: Todo) {
    const index = state.list.findIndex((x) => x.id === id)
    state.list.splice(index, 1)
  }
} as MutationTree<TodoState>

/store/todos/actions.ts

import { ActionTree } from 'vuex'
import { RootState } from '../types'
import { TodoState, Todo } from './types'

export default {
  delete({ commit }, { id }: Todo): void {
    commit('remove', id)
  }
} as ActionTree<TodoState, RootState>

/store/todos/getters.ts

import { GetterTree } from 'vuex'
import { RootState } from '../types'
import { TodoState, Todo } from './types'

export default {
  list(state): Todo[] {
    return state.list
  }
} as GetterTree<TodoState, RootState>

이건 내 컴포넌트를 가지고 있는 코드야

<template>
  <div>
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        {{ todo.text }}
        <button @click="destroy(todo)">delete</button>
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import { mapGetters, mapActions } from 'vuex'

export default Vue.extend({
  computed: {
    ...mapGetters({
      todos: 'todos/list'
    })
  },
  methods: {
    ...mapActions({
      destroy: 'todos/delete'
    })
  }
})
</script>

Vuex에서 발생한 Getter 또는 액션의 자동 완료/인텔리센스 이외에는 모든 것이 완벽하게 동작합니다.

누가 날 도와줄까?

감사합니다.

Vuex는 현재 형식에서 Typescript와 함께 사용할 수 없습니다.Vue 3에서는 이 점이 변경될 수 있습니다.

@Component장식가들, 특히 그들이 비천해졌기 때문에.그러나 기본 Vue 유형 스크립트 구성 요소 스타일을 사용하는 경우:

<script lang="ts">
  import Vue from 'vue';
  export default Vue.extend({...})
</script>

...여러 솔루션을 테스트한 결과, 가장 사용하기 쉬운 것은 실제로 데코레이터를 사용하는 플러그인이라는 것을 알게 되었습니다.vuex-module-decorators

Vuex 모듈:

일반적으로 부모 상태를 클린(빈) 상태로 두고 nameshorized 모듈을 사용합니다.저는 주로 프로젝트의 마지막에 여러 개의 모듈을 설치하는 것이 더 깨끗하고 단순히 추가 모듈을 만드는 것보다 상위 모듈에서 모듈로 이동하는 것이 더 번거롭기 때문에 이 작업을 수행합니다.

스토어는 다음과 같습니다.

import Vue from 'vue';
import Vuex from 'vuex';
import { getModule } from 'vuex-module-decorators';
import Whatever from '@/store/whatever';

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    whatever: Whatever
  }
});

getModule(Whatever, store); // this is important for typescript to work properly

export type State = typeof store.state;
export default store;

다음은 의 몇 가지 예를 제시하겠습니다.mapState,mapGettersget/set:

computed: {
  ...mapGetters({
    foo: 'whatever/foo',
    bar: 'whatever/bar'
  }),
  ...mapState({
    prop1: (state: State): prop1Type[] => state.whatever.prop1,
    prop2: (state: State): number | null => state.whatever.prop2
  }),
  // if i want get/set, for a v-model in template
  baz: {
    get: function(): number {
      return this.$store.state.whatever.baz;
    },
    set: function(value: number) {
      if (value !== this.baz) { // read * Note 1
        this.$store.dispatch('whatever/setBaz', value);
        // setBaz can be an `@Action` or a `@MutationAction`
      }
    }
  }
}

baz can can can can a a a a a a a a a a a a a a a a a a a a로 사용할 수 있게 되었습니다.v-model .메모mapGetters.

import { $http, $store } from '@/main'; // read * Note 2
import { Action, Module, Mutation, MutationAction, VuexModule } from 'vuex-module-decorators';

@Module({ namespaced: true, store: $store, name: 'whatever' })
export default class Whatever extends VuexModule {

  get foo() {
    return // something. `this` refers to class Whatever and it's typed
  }
  baz = 0;
  prop1 = [] as prop1Type[];       // here you cast the type you'll get throughout the app
  prop2 = null as null | number;   // I tend not to mix types, but there are valid cases 
                                   // where `0` is to be treated differently than `null`, so...
  @MutationAction({ mutate: ['baz'] })
  async setBaz(baz: number) {
    return { baz }
  }
}

그럼 이제, 이, 이, 이, 이, 이, 이, 이, 이, now, now, now, now, now, now, now, now, now, now, now, now, now, now, now, now, now, now@Action ★★★★★★★★★★★★★★★★★」@Mutation데코레이터는 그만 두셔도 됩니다. 이치노제가 것들을 사용하고 자신을 발견했어요.@MutationAction이치노해킹을 원하신다면요.
의 내부@MutationAction,this모듈 클래스가 아닙니다.액션 컨텍스트(JS Vuex)

interface ActionContext<S, R> {
  dispatch: Dispatch;
  commit: Commit;
  state: S;
  getters: any;
  rootState: R;
  rootGetters: any;
}

그리고 그건 문제가 아니야.가 Typescript라고 생각한다는 입니다.this가 '모듈 클래스' 안에 ?@MutationAction여기서부터 캐스팅을 시작하거나 타이프가드를 사용할 필요가 있습니다..any. 타이프가드는 멀리 갈 수 있다.
황금률이란 '만약 내가 캐스팅을 해야 한다면'as any또는as unknown as SomeType를 분할해야 한다는 것은 분명한 신호입니다.@MutationAction으로@Action및 a@Mutation하지만 대부분의 경우 활자가드면 충분합니다.예를 들어:

import { get } from 'lodash';
...
@Module({ namespaced: true, store: $store, name: 'whatever' })
export default class Whatever extends VuexModule {
  @MutationAction({ mutate: ['someStateProp'] })
  async someMutationAction() {
    const boo = get(this, 'getters.boo'); // or `get(this, 'state.boo')`, etc...
    if (boo instaceof Boo) {
      // boo is properly typed inside a typeguard
      // depending on what boo is, you could use other typeguards:
      // `is`, `in`, `typeof`  
    }
}

값만 필요한 경우state또는getters:this.state?.prop1 || []또는this.getters?.foo역시 효과가 있습니다.

공평하게 말하자면@MutationAction에는 타입을 선언할 필요가 있기 때문에 타입 해킹이 필요합니다.유형이 올바르게 추론되지 않습니다.따라서 100% 정확성을 유지하려면 상태 속성의 값을 단순히 설정하고 액션과 변환을 모두 작성할 필요가 없는 경우로 사용을 제한합니다.

@MutationAction({ mutate: ['items'] })
async setItems(items: Item[]) {
  return { items }
}

다음으로 대체:

@Action
setItems(items: Item[]) {
  this.context.commit('setItems', items);
  // btw, if you want to call other @Action from here or any @MutationAction
  // they work as `this.someAction();` or `this.someMutationAction()`;
}

@Mutation
setItems(items: Item[]) {
  this.items = items;
}

@MutationAction는 로 등록됩니다.@Actions, 그들은 s를 가져간다.{ mutate: [/* full list of props to be mutated*/]}그리고 변이될 소품 배열에 선언된 모든 국가 소품을 가진 물체를 반환한다.

이상입니다.


* 주의 1: 동일한 2개의 다른 입력(일반 입력과 슬라이더 입력)을 사용할 때 이 체크를 사용해야 합니다.get/set v-model그 체크가 없다면, 각각의 체크가set스택 스위칭에러가 발생합니다.입력이 1개뿐일 때는 보통 체크가 필요하지 않습니다.

* 주의 2: 내 방법은 다음과 같습니다.main.ts으로 닮다

import ...
Vue.use(...);
Vue.config...

const Instance = new Vue({
  ...
}).$mount(App);

// anything I might want to import in components, store modules or tests:
export { $store, $t, $http, $bus } = Instance; 
/* I'd say I use these imports more for correct typing than for anything else 
 (since they're already available on `this` in any component). But they're 
 quite useful outside of components (in services, helpers, store, translation 
 files, tests, etc...)
 */

이 문제에 대한 해결책을 찾다가 이 질문을 발견했습니다.실험을 좀 해봤는데 해결책이 있을 것 같아요.

이라고 하는 은, 그 방법들을 입니다.mapGetters ★★★★★★★★★★★★★★★★★」mapActions따라서 Typescript가 관련된 유형을 추론할 수 있습니다.매퍼에 잘못된 키를 입력하면 컴파일 시간 오류가 발생하며, 보너스로 반환 유형이 정확합니다( 이상 없음).

// returns a type which skips the first context argument
type OmitActionContext<F> = F extends (
  injectee: ActionContext<any, any>,
  payload: infer P
) => infer R
  ? (payload?: P) => Promise<PromiseValue<R>>
  : never;

// signature of action methods
type ActionMethod = (
  injectee: ActionContext<any, any>,
  ...args: any[]
) => Promisable<any>;

/** Typed wrapper for mapActions using a namespaced store and renaming the keys
 *
 *  NOTE: needs to be called with extra parenthesis to infer map keys correctly
 *
 * @example
 *  mapActionsNamespacedWithRename<TYPE>()(namespace, map)
 *
 */
export const mapActionsNamespacedWithRename = <
  S extends Record<keyof S & string, ActionMethod>,
  Keys extends keyof S & string = keyof S & string
>() => {
  function anonymous<Prop extends string, Mp extends Record<Prop, Keys>>(
    namespace: string,
    map: Mp
  ): {
    [P in Keys as GetKeyByValue<Mp, P>]: OmitActionContext<S[P]>;
  };
  function anonymous<Prop extends string, Mp extends Record<Prop, Keys>>(
    namespace: string,
    map: Mp
  ) {
    return mapActions(namespace, map);
  }
  return anonymous;
};

의 래퍼를 사용하면 payload와 Promise 반환 유형을 올바르게 추론할 수 있습니다.

하다, 입력하다, 입력하다, 라고 하면 ./store/todos/actions.ts과 같이

import { ActionContext } from 'vuex'
import { RootState, RootGetters } from '../types'
import { TodoState, Todo } from './types'

export type TodoActionMethods = {
  delete: (injectee: ActionContext<TodoState, RootState>, payload: Todo) => void
}

export default {
  delete({ commit }, payload): void {
    const {id} = payload;
    commit('remove', id)
  }
} as ActionTreeTyped<
  TodoState,
  RootState,
  TodoActionMethods,
  TodoGetters,
  RootGetters
>

컴포넌트에서는 위의 래퍼를 사용해야 합니다.추가된 추가 괄호 및 일반 유형에 주의하십시오.

  methods: {
    ...mapActionsNamespacedWithRename<TodoActionMethods>()("todos", {
      destroy: 'delete'
    })
  }

모듈 증설 없음 - 순수한 Typescript 마법!

완전한 솔루션에 대해서는, 다음의 요점을 참조해 주세요.

언급URL : https://stackoverflow.com/questions/62053067/how-to-get-intellisense-of-mapgetters-mapactions-vuex-and-typescript-without-cl

반응형