title: Extending Vue instance interface in TypeScript Quasar boot file
body: <p><strong>Edit</strong>: I said several times in the post below that I'm &quot;not in a module&quot;. Any time JavaScript code in one file is called from another that's technically a module. What I meant to say was that this module is called automatically by Quasar at script initialization time and is expected to have global side effects. The module is never imported into any code that I write.</p>
  <h2>Succint post:</h2>
  <p>I'm using Quasar 2.0.0 with TypeScript support and Vue Class Components. I wish to add an instance property to be accessible in my classes as: <code>this.$fetch(...);</code></p>
  <p>I've created a boot file with the following:</p>
  <pre><code>import Vue from &quot;vue&quot;;
  
  declare module &quot;vue/types/vue&quot;
  {
      interface Vue
      {
          prototype: {
              $fetch: (resource: (string | Request), init?: RequestInit) =&gt; Promise&lt;Response&gt;
          },
          $fetch: (resource: (string | Request), init?: RequestInit) =&gt; Promise&lt;Response&gt;
      }
  }
  
  type Context = {
      Vue: Vue
  };
  
  export default async ({ Vue: vue }:Context) =&gt;
  {
      vue.prototype.$fetch = async (resource, init) =&gt;
      {
          return fetch(resource, init);
      };
  };
  </code></pre>
  <p>This doesn't throw any errors with TypeScript. But when I try to use it in a component, I get an error:</p>
  <pre><code>&lt;template&gt;
      &lt;q-layout&gt;
          &lt;q-page-container&gt;
              &lt;q-page class=&quot;flex flex-center&quot;&gt;
                  &lt;h1&gt;Test&lt;/h1&gt;
              &lt;/q-page&gt;
          &lt;/q-page-container&gt;
      &lt;/q-layout&gt;
  &lt;/template&gt;
  
  &lt;script lang=&quot;ts&quot;&gt;
  import { Vue, Component } from &quot;vue-property-decorator&quot;;
  
  @Component
  export default class TestPage extends Vue
  {
      mounted ()
      {
          this.$fetch(&quot;https://www.google.com&quot;);
      }
  }
  &lt;/script&gt;
  </code></pre>
  <blockquote>
  <p>Property '$fetch' does not exist on type 'TestPage'.</p>
  </blockquote>
  <h2>Previous (rambly) post:</h2>
  <p>I'm working on a Quasar app and as a convenience I'd like to create a modified <code>fetch</code> method that injects my API key if the requested domain matches my back-end domain. The interface would be something like:</p>
  <pre><code>@Component
  export default class VueComponent extends Vue
  {
      mounted()
      {
          this.$fetch(&quot;https://www.example.com/api/endpoint&quot;).then(console.log);
      }
  }
  </code></pre>
  <p>I know how to define something like this. I just need to update the <code>Vue</code> prototype, per <a href="https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html" rel="nofollow noreferrer">https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html</a>:</p>
  <pre><code>Vue.prototype.$fetch = async function(request, init)
  {
      // ... blah ...
  };
  </code></pre>
  <p>Per <a href="https://quasar.dev/quasar-cli/boot-files#When-to-use-boot-files" rel="nofollow noreferrer">Quasar's documentation</a>, the proper time to update the Vue prototype is in a <em>boot file</em>. So I created one:</p>
  <pre><code>quasar new boot fetch
  </code></pre>
  <p>My boot file started out fairly simple:</p>
  <pre><code>export default async ({ Vue }) =&gt;
  {
      Vue.prototype.$fetch = async (resource, init) =&gt;
      {
          return fetch(resource, init);
      };
  };
  </code></pre>
  <p>When everything fell apart is when I tried to convert to TypeScript. The parameter <code>Vue</code> implicitly has an <code>any</code> type, because there's no type provided to the parameter object.</p>
  <p>I tried defining the type like so:</p>
  <pre><code>import Vue from &quot;vue&quot;;
  
  type Context = {
      Vue: Vue
  };
  
  export default async ({ Vue }:Context) =&gt;
  </code></pre>
  <p>However this caused complains about variable shadowing (<code>Vue</code> is defined in the upper scope). I noticed that the <code>Vue</code> variable had a type of <code>VueConstructor&lt;Vue&gt;</code>, so I instead tried:</p>
  <pre><code>import { VueConstructor } from &quot;vue&quot;;
  
  type Context = {
      Vue: VueConstructor
  };
  
  export default async ({ Vue }:Context) =&gt;
  </code></pre>
  <p>That worked for the function definition, but threw an error on the <code>Vue.prototype.$fetch = ...</code> line: &quot;Unsafe member access .$fetch on any value&quot;. TypeScript doesn't expect <code>Vue.prototype</code> to have a <code>$fetch</code> property. If I were making a module definition I would extend the interface like so:</p>
  <pre><code>declare module &quot;abc&quot; {
      VueConstructor: {
          prototype: {
              $fetch: ...
          }
      }
  }
  </code></pre>
  <p>However I'm not declaring a module -- this is a boot file. I tried a quick hack to work around this, defining my own <code>VueConstructor</code> only within the context of my boot file:</p>
  <pre><code>interface VueConstructor
  {
      prototype: {
          $fetch: (resource: (string | Request), init?: RequestInit) =&gt; Promise&lt;Response&gt;
      }
  }
  
  type Context = {
      Vue: VueConstructor
  };
  
  export default async ({ Vue }:Context) =&gt;
  {
      Vue.prototype.$fetch = async (resource: (string | Request), init?: RequestInit) =&gt;
      {
          return fetch(resource, init);
      };
  };
  </code></pre>
  <p>This finally got TypeScript to stop complaining about <em><strong>this</strong></em> file, but the moment I try to utilize <code>this.$fetch</code> in a component:</p>
  <blockquote>
  <p>Property '$fetch' does not exist on type 'VueComponent'.</p>
  </blockquote>
  <p>Since <code>VueComponent</code> extends <code>Vue</code> I think I need to update the definition of the <code>Vue</code> class, but I'm lost on how to do this. I tried adding the following to my boot file:</p>
  <pre><code>interface Vue
  {
      $fetch: (resource: (string | Request), init?: RequestInit) =&gt; Promise&lt;Response&gt;
  }
  </code></pre>
  <p>But this didn't work. I can't just use <code>declare module</code> because, again, I'm not in a module. How do you extend a type globally without a module?</p>