86

I'm currently using Vue.js with Typescript in a webpack project.

As indicated in the Recommended Configuration in my tsconfig.json I have:

"strict": true,

Inside one of my component i have:

declare interface Player {
    cod: string,
    param: string
  }

export default Vue.extend({
    name: 'basecomponent',
    data() {
      return {
        players: []
      };
    },
    created() 
      let self = this
      axios.get('fetch-data')
        .then((response) => {
          let res: Players[] = response.data;
          for(let i = 0; i < res.length; i++){
              self.players.push(res[i]);
          }
        })
        .catch((error: string) => {
          console.log(error);
       });
    },
 });

but when I try to compile i get:

 error TS2345: Argument of type 'Player' is not assignable to parameter of type 'never'.

Cause I believe players: [] has never[] type.

My question is: how can I infer type Vue data object properties??

7 Answers 7

144

To add to Joshua's answer, you may want to declare the type of players inline so your code doesn't get too verbose as your data gets larger.

data() {
  return {
    players: [] as Player[]
  };
},

another option:

data() {
  return {
    players: new Array<Player>()
  };
},
Sign up to request clarification or add additional context in comments.

3 Comments

Except, least with my vanilla setup I get a warning - The class method 'data' must be marked either 'private', 'public', or 'protected'. I assume we drop a get in front of it?
i now get the 'type assertion on object literals' error with this approach. the approach below from @ore4444 is now the more current recommendation
the type assertion is not a good idea. Someone could even write players: [{notAPlayer: true}] as Player[]
34

This should work:

declare interface Player {
  cod: string,
  param: string
}

declare interface BaseComponentData {
  players: Player[]
}

export default Vue.extend({
  name: 'basecomponent',
  data(): BaseComponentData {
    return {
      players: []
    };
  },
})

1 Comment

the sad side is that for every new key you'll have to declare it twice, once in the interface and another on data function
29

Your data method has an undeclared return value.

If you supply one, TypeScript will know what to expect with players.

You just need to expand the data() { line.

e.g.:

data() {
  return {
    players: []
  };
},

needs to become:

data() : {
  players: Array<any>, // if possible, replace `any` with something more specific
} {
  return {
    players: []
  };
},

Tada! players is now of type Array of any.

4 Comments

I do not think I have understood the proposed solution, in any case your code is semantically wrong, ''data'' must be a function that return an object...
it's not generic typescript but typescript used on a VUE object
@Plastic It is right usage of typescript. It shows return type of data function so that you can declare the types of return value.
I think the first word of the second line of your proposed code should read players, not people.
13

I found another method that is more close to the typical syntax, while keeping the code short.

data() {
  return new class {
    players: Player[] = []
  }();
},

2 Comments

This is arguably the best way to do it. This is declaring the type with shorthand syntax, and assigning the value in one line. Using as does type coercion, probably not what you want, while new Array<T>() is the generic (uses generics) syntax of T[].
I like this method. How do you solve the issue of assigning values from props? props: {initialPlayer: Object}, data() { return new class { players: Player[] = [this.initialPlayer] // this would refer to the class? }(); },
8

Type assertion using the '<>' syntax is forbidden. Use the 'as' syntax instead.

It will look like this:

players: [] as Player[]

2 Comments

Any source you could refer to, backing up your claim that '<>' syntax is forbidden?
@nomadoda It is not forbidden, i think Илья Карев is referring to TSlint warning you for the use of <T> for type casting, the reason why you should prefer the myVar as T Syntax is due to lacking JSX support. You can read that in the TypeScript docs typescriptlang.org/docs/handbook/jsx.html
5

In case anyone comes across this in the future, here is the answer that solved my problem. It is a little more "wordy", but it does the type inference properly everywhere within the Vue.extend() component definition:

interface Player {
  cod: string,
  param: string
}

// Any properties that are set in the `data()` return object should go here.
interface Data {
  players: Player[];
}

// Any methods that are set in the "methods()" should go here.
interface Methods {}

// Any properties that are set in the "computed()" should go here.
interface Computed {}

// Any component props should go here.
interface Props {}

export default Vue.extend<Data, Methods, Computed, Props>({
    name: 'basecomponent',
    data() {
      return {
        players: []
      };
    },
    // You probably will want to change this to the "mounted()" component lifecycle, as there are weird things that happen when you change the data within a "created()" lifecycle.
    created() {
      // This is not necessary.
      // let self = this
      // If you type the Axios.get() method like this, then the .data property is automatically typed.
      axios.get<Players[]>('fetch-data')
        .then(({ data }) => {
          // This is not necessary.
          // let res: Players[] = response.data;
          // for(let i = 0; i < data.length; i++){
          //     self.players.push(data[i]);
          // }
          this.players = data;
        })
        .catch((error: string) => {
          console.log(error);
       });
    },
 });

Comments

0

To not resort to describing the whole data structure, a more viable approach would probably be to first define players as a variable with explicit type:

export default Vue.extend({
    name: 'basecomponent',
    data() {
      const players: Player[] = [];
      return {
        players: players
      };
    },
...

This should work for other complex types as well, not just for arrays. Though I'm yet to find out how to tell volar not to narrow the union type by the initializer value -_-

1 Comment

You can simplify the code by directly returning the players array without explicitly assigning it to a property with the same name. return { players }

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.