vue-class-component : how to create and initialize reflective data
Asked Answered
P

2

7

I'm using vuejs with typescript and vue-class-component. I'm trying to create a custom component with reactive data.

Here is my code:

<template>
    <div>
        <input v-model="data.name" placeholder="Name">
        <input v-model="data.value" placeholder="Value">
    </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

interface Model {
    name: string;
    value: number;
}
@Component
export default class ClubVue extends Vue {
    private data: Model;

    public mounted() {
        this.data = {...this.$store.getters.data};
    }
}
</script>

In this first version, I've got this error :

Property or method "data" is not defined on the instance but referenced during render

That's normal, as said in vue-class-component page, undefined data won't be reflective. I need to initialize data to null. Furthermore, i get this typescript error:

Property 'data' has no initializer and is not definitely assigned in the constructor

So I want to do this:

private data: Model = null;

But I get this typescript error:

Type 'null' is not assignable to type 'Model'.

I don't wan't to change the data type to Model | null because I would have to check if data is null everywhere I will use it, and I know that data will never be null.

private data!: Model;

Does not work either because data will be undefined and so won't be reactive.

I don't want to turn off typescript checks because they are useful for other parts of code.

Is there a proper way to initialize data here?

Pasha answered 16/4, 2019 at 19:8 Comment(0)
P
13

A correct type for such property is:

private data: Model | null = null;

It can be used with type guards:

if (this.data) {
  console.log(this.data.name); // Model
}

Or non-null assertion operator:

console.log(this.data!.name); // Model

A workaround is to cheat typing system with assertion:

private data: Model = null as unknown as Model;

vue-class-component doesn't take TypeScript into account because undefined is easier to handle in TypeScript than null, particularly because this would allow to mark a property as optional.

I know that data will never be null.

It will be null until the component is mounted, this leaves a room for mistake:

public created() {
    console.log(this.data.name); // runtime error but no compilation error
}

public mounted() {
    this.data = {...this.$store.getters.data};
}
Plagiarism answered 26/6, 2019 at 6:11 Comment(0)
T
2

You can use a getter instead of assigning the data directly, assuming the data is set in the store when it is created. Make sure your store getters are typesafe!

@Component
export default class ClubVue extends Vue {
    private _data: Model | undefined;

    get data(): Model {
        if (!this._data) {
            this._data = {...this.$store.getters.data};
        }
        return this._data;
    }
}

This way, data will never be undefined, since it will either return _data or set _data to the current store content and then return that.
This may fail if _data is a primitive instead of an object, and evaluates to false (e.g. (Number)0 or (String)""). In that case, use this._data === undefined instead of !this._data.

You can also shorten the getter to

get data():Model {
    return this._data = this._data || {...this.$store.getters.data};
}

but this is less clear, especially if the reader is not aware that an assignment will return the value/reference that is being assigned, and even worse to read with primitive types:

return this._data = 
    this._data === undefined 
        ? {...this.$store.getters.data} 
        : this._data;
Teri answered 26/6, 2019 at 9:7 Comment(2)
I support this approach, yet typeof(this._data) === 'undefined' is redundant, this._data === undefined is more readable but unnecessary too because it can be either undefined or an object in this snippet. As for private _data: Model | undefined, it can be shortened to private _data?: Model.Plagiarism
Yep, in this snippet the typeof is unneccessary, I mentioned it to make sure that readers are aware of primitive types when this is adapted to fit other code. I agree on the shortening of the undefined check and will edit the post to fit it. Although I will leave the _data declaration as I wrote it, mainly because it makes it clearer that it will be undefined initially. While I usually use the optional parameter in my code, I prefer longer, more explicit forms that make it easier to grasp when writing code on this platform - makes it easier for beginners to understand. Thank you!Teri

© 2022 - 2024 — McMap. All rights reserved.