vue: Vuejs type inference does not work with TS 3.4.x

Version

2.6.10

Reproduction link

https://github.com/Patcher56/ts3.4.x-vuejs-issue

Steps to reproduce

  1. create a new vue typescript project (without class syntax) vue create test-ts
  2. open src/components/HelloWorld.vue
  3. add a data variable and try to access it in a computed property

What is expected?

No errors (as the data is defined)

What is actually happening?

Typescript error 2339 telling me Property 'test' does not exist on type 'CombinedVueInstance<Vue, {}, {}, {}, Readonly<{ msg: string; }>>'.

image


I think this has something to do with the changed type inference in Typescript 3.4.x.

Important: Change vetur settings to use workspace dependencies to show errors directly in vscode, else you will only get the error when using yarn serve.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 10
  • Comments: 27 (12 by maintainers)

Most upvoted comments

One workaround is assigning a return type to computed

type TypedDef<Data, Computed> =
  ComponentOptions<Data, Computed> &
  ThisType<Data & Computed>

type DataDef<Data> = () => Data

export interface ComponentOptions<Data, Computed> {
  data?: DataDef<Data>
  computed?: Accessors<Computed>
}
export type Accessors<T> = {
  [K in keyof T]: () => T[K]
}

declare function component<Data, Computed>(def: TypedDef<Data, Computed>): void;

component({
  data() {
    return {
      foo: 23
    }
  },
  computed: {
    bar(): number { // adding the return type, seems to solve the error
      return this.foo + 1
    }
  }
})

Best workaround is to use the new Vue Composition API. It is a lot cleaner for typescript and works also with typescript versions > 3.3.4000.

The best way to avoid the issue now is to annotate return type of computed method… We cannot make much progress since it is an upstream issue in TS.

Unfortunately it also happens if you have a validator on a prop, and it isn’t fixed with the annotation workaround:

export default Vue.extend({
  props: {
    foobar: {
      required: true,
      type: String,
      validator(foobar: string): boolean { return ['foo', 'bar'].includes(foobar); },
    },
  },

  data(): { initialFoobar: string } {
    return {
      initialFoobar: this.foobar, //=> Property 'foobar' does not exist on type 'CombinedVueInstance<Vue, {}, {}, {}, Readonly<{}>>'
    };
  },

  computed: {
    foobang(): string {
      return `${this.foobar}!`; //=> Property 'foobar' does not exist on type 'CombinedVueInstance<Vue, {}, {}, {}, Readonly<{}>>'
    },
  },
});

Breaks our entire build, easily 100+ errors after stepping up from TS v3.3.3333 to v3.4.4. Too bad, too, because this version of TS introduced some handy features for exactly what I’m working on right now… 😩

Looks like TS3.4 has multiple inference issue…

The minimal repro:

Vue.component('test', {
  data() {
    return {
      test: 123
    }
  },
  computed: {
    ttt() {
      return this.test + 1
    }
  },
})

Also spotted another failure:

The catch-all type signature of component

  component(id: string, definition?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>;

is causing union type to fail checking.

I need more time for investigation…

We also had a component start throwing a lot of errors after upgrading from TS 3.3.3333 to 3.4.5

Thanks to this comment, went back and checked that component and, sure enough, one of the computed properties did not have a return type annotated. With that annotation added, the build compiled without issue.

Per the TS comment, perhaps the docs would be best updated as “you will need to annotate the return type on methods like render and those in computed” (though the docs are already pretty clear on the cases that are now breaking)

I wonder if properties in data are supposed to become properties of the instance when using TypeScript?

(Note: the two examples I gave are not fixed by the workaround mentioned)