vuex-smart-module icon indicating copy to clipboard operation
vuex-smart-module copied to clipboard

Return type of actions is Promise<unknown>

Open nicolidin opened this issue 3 years ago • 2 comments

Hi @ktsn

I noticed that every return type of action method is considered as Promise<unknow>. Even though if the action return type is Promise<SpecificType>.

I think you did it on purpose, but I don't know how can I avoid the following error, I mean what is the best way to avoid it?

Whenever I return a Promise<SpecificType>, I get this kind of error: TS2345: Argument of type '(response: AxiosResponse<any>) => void' is not assignable to parameter of type '(value: unknown) => void | PromiseLike<void>'.

nicolidin avatar Jul 20 '21 10:07 nicolidin

I mean what is the best way to avoid it

Hi, normally, you shouldn't return a value from an action, preferring to put it in the store. Otherwise, you are going to lose the meaning of store as the single source of truth.

interface AnyNetworkData {
  loading: boolean,
  error: string | null,
  data: ExampleDataType | null,
}

export class ExampleState {
  anyNetworkData: AnyNetworkData = {
    loading: false,
    error: null,
    data: null,
  }
}

export class ExampleGetters extends Getters<ExampleState> {}

export class ExampleMutations extends Mutations<ExampleState> {
  setAnyNetworkData(payload: AnyNetworkData): void {
    this.state.anyNetworkData = { ...payload };
  }
}

export class ExampleActions extends Actions<
  ExampleState,
  ExampleGetters,
  ExampleMutations,
  ExampleActions
> {
  async fetch(): Promise<void> {
    this.mutations.setAnyNetworkData({ loading: true, error: null, data: null });
    
    try {
      const response = await ApiClient.doSomething();

      this.mutations.setAnyNetworkData({ loading: false, error: null, data: response.data });
    } catch ({ message }) {
      this.mutations.setAnyNetworkData({ loading: false, error: message, data: null });
    }
  }
}

vdkrasny avatar Aug 10 '21 12:08 vdkrasny

First i wanna thank you for great module, it really cool!

Return type for action promise can be very usefully:

Example 1

interface Item {
  id: number | null;
  name :string;
}

export interface Raw {
  // eslint-disable-next-line
  [key: string]: any;
}

const parseItem = (data: Raw): Item => ({
  id: data['id'] ? Number(data['id']) : null,
  name: String(data['name']),
})

export class ExampleActions extends Actions<
  ExampleState,
  ExampleGetters,
  ExampleMutations,
  ExampleActions
> {
    createItem(item: Item): Promise<Item> {
      return api.create(item)
        .then(({ data }) => {
          const newItem = parseItem(data.data);
          this.mutations.setItem(newItem);
          return newItem;
        })
  }
}

In component:

onSave(): void {
    this.exampleStore.actions.createItem(item)
      .then((item: Item) => {
        // here I can use the item without access to store
        // Without item, I would not have been able to find it in a store as I did not know its id before creating it.

        this.activeItem = item;
      })
}

Example 2

export class ExampleActions extends Actions<
  ExampleState,
  ExampleGetters,
  ExampleMutations,
  ExampleActions
> {
  /**
   * Check item in store before load it.
   * This is useful to avoid unnecessary requests to the api and reuse of logic in different components.
   *
   * @param itemId
   */
  loadItem(itemId: number): Promise<Item> {
    return this.state.items[itemId]
      ? Promise.resolve(this.state.items[itemId])
      : api.loadItem(itemId)
        .then(({ data }) => {
          const newItem = parseItem(data.data);
          this.mutations.setItem(newItem);
          return newItem;
        })
  }
}

Nemhis avatar May 04 '22 12:05 Nemhis