Search code examples
arraystypescriptvue.jsvuextypescript-typings

"Not assignable to parameter of type never" TS error in Vue store array declaration


I don't understand why I'm getting this error:

Argument of type '{ id: string; }' is not assignable to parameter of type 'never'.

... at the line const index = state.sections.findIndex((section) => section.id === id);

in the following portion of my Vue store:

import { MutationTree, ActionTree, GetterTree } from 'vuex';

interface section {
  id: string;
  section_header: string;
  section_body: string;
  sectionItems: any;
}

interface sections extends Array<section>{}

export const state = () => ({
  sections: [
    {
      id: '1',
      section_header: 'lorem',
      sectionItems: []
    },
    {
      id: '2',
      section_header: 'ipsum',
      sectionItems: []
    }
  ]
});

type EditorState = ReturnType<typeof state>;

export const mutations: MutationTree<EditorState> = {
  ADD_SECTION_ITEM(state, id) {
    const index = state.sections.findIndex((section) => section.id === id);
    const sectionItem = {
      id: randomId()
    }
    state.sections[index].sectionItems.push(sectionItem);
  },
};

Solution

  • When you don't provide a type for a value -- your function's return value, in this case -- TypeScript tries to narrow it down to the most accurate type it can. Since your returned object's sections' item arrays are always empty, it infers them as never[] (type[] is an alias for Array<type>); an array that can't actually contain any values. You can see this in the following snippet:

    const functionReturningEmptyArray = () => ({ items: [] });
    type EmptyArray = ReturnType<typeof functionReturningEmptyArray>;
    // type EmptyArray = { items: never[] }
    

    One way you can solve this is using the section interface you created to specify the return type of state.

    export const state = (): { sections: Array<section> } => ({
      sections: [
        {
          id: '1',
          section_header: 'lorem',
          sectionItems: []
        },
        {
          id: '2',
          section_header: 'ipsum',
          sectionItems: []
        }
      ]
    });
    

    The colon after the empty parentheses on the first line specifies the function's return type. I've inlined the object type in this case as it only has one property, but if your state is more complex, or if you just prefer the readability, you can extract it to a separate type and reference that as the return type;

    interface MyState {
        sections: Array<section>;
    }
    
    export const state = (): MyState => ({
      sections: [
        {
          id: '1',
          section_header: 'lorem',
          sectionItems: []
        },
        {
          id: '2',
          section_header: 'ipsum',
          sectionItems: []
        }
      ]
    });
    

    At this point, TypeScript will throw an error on your returned value, because your section interface specifies that the object should also have a section_body property, which is missing in your returned sections array. To fix this, you can either give each object in the array a section_body property, or modify the interface to match the fact that the property may or may not exist;

    interface section {
      id: string;
      section_header: string;
      section_body?: string;
      sectionItems: any;
    }
    

    Your store should now be error-free, but to have a more type safe sectionItems property, you can also change it from any to Array<any>, or use a more specific object type if you know what the section items will look like beforehand.