Typescript: Regression in Vue typings in 3.4.x

Created on 11 Apr 2019  Β·  6Comments  Β·  Source: microsoft/TypeScript


TypeScript Version: 3.4.1, 3.4.2, 3.4.3, 3.5.0-dev.20190410


Search Terms: Vue 3.4 typing

Code

It's not clear to me if this is a problem with Typescript or with Vue's types, but it started happening in Typescript 3.4 with no change to Vue.

I have a repo that reproduces the problem here: https://github.com/mrozekma/typescript-vue-problem. This was created using Vue CLI with just Typescript enabled, followed by upgrading to Typescript 3.4 and adding the following to the default export in src/App.vue:

  computed: {
    foo: function() {
      return this.bar;
    },
  },
  data: function() {
    return {
      bar: true,
    };
  },

Expected behavior:

Should build without errors.

Actual behavior:

This builds fine with 3.3.3, and according to Visual Studio Code it's valid (even version 1.33.0 with bundled Typescript 3.4.1), but if I try to build with any Typescript 3.4.x I get:

ERROR in src/App.vue
19:19 Property 'bar' does not exist on type 'CombinedVueInstance<Vue, {}, {}, {}, Readonly<Record<never, any>>>'.
    17 |   computed: {
    18 |     foo: function() {
  > 19 |       return this.bar;
       |                   ^
    20 |     },
    21 |   },
    22 |   data: function() {

The type of this on line 19 is unexpectedly sparse. The second template argument should be the return type of the data function, and looks correct in Code's tooltip of that line:

CombinedVueInstance<Vue, {
    bar: boolean;
}, {}, {
    foo: boolean;
}, Readonly<Record<never, any>>>

Playground Link:

Related Issues:

Possibly related to #30442, as it mentions a regression with intersection types, which ComputedVueInstance is.

Working as Intended

Most helpful comment

The solution does work for this case but if the computed property would reference a prop the error is not solvable by adding a return type.

Example:

export default Vue.extend({
  props: {
    value: {
      required: true,
      type: String as PropType<string>
    }
  },
  computed: {
    myComputed (): string {
      return this.value // Property 'value' does not exist on type 'CombinedVueInstance... 
  }
}

All 6 comments

Minimal repro here: https://github.com/vuejs/vue/issues/9873#issuecomment-483491583

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() {
      return this.foo + 1 // comment out the return solves the problem
    }
  }
})

Looks like a case of #18805 to me.

Strange that it worked at all before 3.4, since I've been seeing this issue for a long time (as described in #18805). I'm now in the habit of adding explicit return type annotations to my vue computeds and methods to work around it.

Looks like a case of #18805 to me.

Adding the return type to foo in my test repo fixed it, and similar changes in my real project fixed all the problems that popped up when I originally tried to upgrade to 3.4.3, so I think you're right. 3.4.x definitely became more sensitive to the issue though, since both repos were working on 3.3.3.

I'm surprised this ever worked without a type annotation. It certainly was not intended to. In the examples above, when a method without a return type annotation references this in a return statement, we end up needing to know the this type at the same time we're making the inferences that inform the this type. In other words, we have a circularity.

I know that we've gotten better at being consistent in the face of circularities in 3.4, in the sense that we _consistently_ produce the same types for entities during all phases of inference and in quick info. I'm not exactly sure why the example worked in 3.3, and it would take some debugging to find out. But either way, 3.4 behaves as intended and the solution is to add return type annotations to methods that reference this in their return expressions.

The solution does work for this case but if the computed property would reference a prop the error is not solvable by adding a return type.

Example:

export default Vue.extend({
  props: {
    value: {
      required: true,
      type: String as PropType<string>
    }
  },
  computed: {
    myComputed (): string {
      return this.value // Property 'value' does not exist on type 'CombinedVueInstance... 
  }
}

computed's return type is required for ts? why?

I don't want to add the return type...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Zlatkovsky picture Zlatkovsky  Β·  3Comments

jbondc picture jbondc  Β·  3Comments

fwanicka picture fwanicka  Β·  3Comments

kyasbal-1994 picture kyasbal-1994  Β·  3Comments

siddjain picture siddjain  Β·  3Comments