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?

2 Answers 2

13
+50

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};
}
Sign up to request clarification or add additional context in comments.

Comments

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;

2 Comments

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.
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!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.