vue-async-computed-decorator icon indicating copy to clipboard operation
vue-async-computed-decorator copied to clipboard

Async computed getter

Open boukeversteegh opened this issue 3 years ago • 4 comments

This PR adds support to use @AsyncComputed() on getter methods:

  @AsyncComputed()
  get user(): User {
    return fetch('/api/users/me')
       .then(r => r.json())
       .then(data => data.user) as any
  }

This allows for greatly improved type-checking and IDE support for AsyncComputed.

Benefits:

  • You can force the return type to be User instead of Promise<User>, which is what the eventual type will be.
    • This trick is only possible with getters, because all async functions must return a Promise.
  • Editors will understand this.user.name and will not expect this.user().name as is the case now.
  • Otherwise, the plugin works exactly the same as with async functions. All options supported.
  • We can use async-computed in a mostly type-safe manner without much boilerplate.

Drawbacks:

  • await is not allowed in getters, so you must use only .then and .catch
  • the intermediate cast to any is not type-safe, and may hide incorrect casts.

To work around these limitations, I suggest the following helper method. It is not included, but it could be if approved.

// Cast a Promise<T> to T.
// Accepts a promise or async function.
function asyncComputed<T>(promise: Promise<T> | (() => Promise<T>)): T {
  return (typeof promise === 'function' ? promise() : promise) as any
}

This can be used as follows:

  @AsyncComputed()
  get users(): User {
    return asyncComputed(fetchUserAsync()) // safely cast Promise<User> to User
  }

  @AsyncComputed()
  get users(): User {
    return asyncComputed(async () => {
      return await fetchUserAsync() // using await within the getter
    })
  }

boukeversteegh avatar Feb 02 '22 17:02 boukeversteegh