TypeScript support

Now, let's take a look at an example of leveraging TypeScript support.

Let's assume that we have the following component tree:

If we want to define a dependency that can be injected into the UserSettings component, then we can introduce a provider for it in any of its ancestors: Settings, Content, or App. Providers that are defined at the App level can be used anywhere in the component tree.

Once again, we can draw a parallel with Angular features since this provider hierarchy is somewhat similar to Angular's injector tree.

In the following example, we'll simply make a username string available for injection, but we could do the same with any other object type:

<template> 
    <div id="app"> 
        <PageHeader /> 
        <Content /> 
        <PageFooter /> 
    </div> 
</template> 
 
<script lang="ts"> 
    import {Component, Vue} from 'vue-property-decorator'; 
    import PageHeader from '@/components/PageHeader.vue'; 
    import PageFooter from '@/components/PageFooter.vue'; 
    import Content from '@/components/Content.vue'; 
 
    @Component({ 
        components: { 
            PageHeader, 
            Content, 
            PageFooter, 
        }, 
        provide: { 
            'username': 'dSebastien', 
        }, 
    }) 
    export default class App extends Vue { 
    } 
</script> 
 
<style> 
</style> 

We have simply used the provide property to define the value.

Take note of how the App component registers the PageHeaderContent, and PageFooter components. These are registered locally, as opposed to the global registration approach that we used earlier using Vue.component(...​). The advantage of declaring component dependencies locally is that it enables additional build optimizations, such as tree shaking and better code splitting: https://vuejs.org/v2/guide/components-registration.html#Local-Registration.

If you pay attention to the TypeScript imports, you'll see that there is a weird leading @ symbol before the path to the file to import. This is actually a TypeScript path mapping that's defined for us in the TypeScript compiler configuration (that is, in tsconfig.json) by the Vue CLI. We covered this concept in Chapter 5, Coding WorldExplorer to Explore the Population of the World.

Here is how we can inject the value into the UserSettings component:

<template> 
    <div class="header"> 
        <h3>User settings</h3> 
        <span>Username: {{username}}</span> 
        <div> 
            <label> Likes chocolate? 
                <input type="checkbox" value="Like chocolate" v-
model="likeChocolate" /> </label> </div> </div> </template> <script lang="ts"> import {Component, Inject, Vue} from 'vue-property-decorator'; @Component({}) export default class UserSettings extends Vue { @Inject('username') username!: string; // definite assignment assertion likeChocolate: boolean = true; } </script> <style scoped> </style>

As you can see, we've leveraged the vue-property-decorator library again to achieve more idiomatic TypeScript code. Injecting using a decorator feels natural if you've used Angular or the Spring framework in Java before.

In fact, vue-property-decorator also includes a decorator called @Provide. Here's how we can use it to declare a similar provider in a more TypeScript-friendly way:

export default class PageHeader extends Vue { 
    @Provide("menu-title") 
    menuContents: { title: string } = { title: "Foo" }; 
} 

These examples are contrived, but convey how things work out of the box with Vue and TypeScript.

You can find these code samples under Chapter09/06-vue-injection-basic.

By taking advantage of this basic dependency injection support, you can improve the maintainability of your code.