Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression in Vue typings in 3.4.x #30854

Closed
mrozekma opened this issue Apr 10, 2019 · 7 comments
Closed

Regression in Vue typings in 3.4.x #30854

mrozekma opened this issue Apr 10, 2019 · 7 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@mrozekma
Copy link

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.

@HerringtonDarkholme
Copy link
Contributor

Minimal repro here: vuejs/vue#9873 (comment)

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
    }
  }
})

@yortus
Copy link
Contributor

yortus commented Apr 16, 2019

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.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Apr 18, 2019
@mrozekma
Copy link
Author

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.

@ahejlsberg
Copy link
Member

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.

@sem4phor
Copy link

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

@musicode
Copy link

musicode commented Jul 8, 2019

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

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

@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 3.6.0 milestone Jul 16, 2019
@RyanCavanaugh RyanCavanaugh removed their assignment Jul 16, 2019
@RyanCavanaugh RyanCavanaugh added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Needs Investigation This issue needs a team member to investigate its status. labels Aug 22, 2019
@andrea-cerlini
Copy link

andrea-cerlini commented Jun 16, 2021

But shouldn't it just understand the correct type whenever there's the need, based on the code context, and the fact that data() and computed() properties are in fact properties of the instance?
When any component-specific variable (property) is created, VSCode's TypeScript extension does understand the correct type of it, so the programmer expects that there's no need to make it explicit, because there's no error shown (might instead be because vetur doesn't automatically show that type of errors??).
But that doesn't seem to work the same way at build time, for example with data() or computed() properties: the compiler builds, and everything runs as expected, but the console reports these errors.

Edit: Nevermind, there is such a simple way to do it, and it's to extend Vue so that the properties get correctly "linked" to the component instance itself... I just had not found the answer before writing this, so I guess just forget what I've said lol

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

8 participants