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

Improve Vue type declarations for more canonical usage #5887

Closed
wants to merge 22 commits into
base: dev
from

Conversation

Projects
None yet
@DanielRosenwasser

DanielRosenwasser commented Jun 14, 2017

This PR improves the Vue .d.ts files so that users can get some improved inference when using the noImplicitThis compiler option. It takes advantage of many of the features the TypeScript team has been working on over the last few versions. Thus, it requires a minimum TypeScript version of 2.4. You can get this working with the current typescript@next (which is 2.5.0-dev.20170614, but don't let that version number fool you - this will work with the final release of 2.4).

The best part about this change is that it makes using Vue easier when users choose not to use classes and inheritance. In other words, users and can more easily new up an instance using the Vue constructor, and can create components using the Vue.extend and Vue.component APIs a little more naturally. Extending Vue as a class will still work, so if you prefer to use vue-class-component and the rest, that should still work! However, those dependencies may need to be updated for this change.

There's definitely some amount of complexity that's been introduced but I'm willing to iterate on this with the help of others. Given the feedback that we've heard on TypeScript-Vue-Starter, it's been a positive experience for users.

Fixes many of the issues users have asked about in #478.

Tests are probably failing at this point because I need to update the project's dependency on TypeScript.

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Jun 15, 2017

Member

Thanks @DanielRosenwasser ! Are there any remaining incompatibilities aside from the need for using latest dependancies, e.g. required change in tsconfig?

Also this would cover #5016 as far as I can tell.

Member

yyx990803 commented Jun 15, 2017

Thanks @DanielRosenwasser ! Are there any remaining incompatibilities aside from the need for using latest dependancies, e.g. required change in tsconfig?

Also this would cover #5016 as far as I can tell.

@HerringtonDarkholme

This comment has been minimized.

Show comment
Hide comment
@HerringtonDarkholme

HerringtonDarkholme Jun 15, 2017

Member

hmmm, the test is failing.

It seems Base constructor return type 'CombinedVueInstance<Data, Methods, Computed, Record<PropNames, any>>' is not a class or interface type..

So interface augmentation fails.

Member

HerringtonDarkholme commented Jun 15, 2017

hmmm, the test is failing.

It seems Base constructor return type 'CombinedVueInstance<Data, Methods, Computed, Record<PropNames, any>>' is not a class or interface type..

So interface augmentation fails.

propsData?: Object;
computed?: { [key: string]: ((this: V) => any) | ComputedOptions<V> };
methods?: { [key: string]: (this: V, ...args: any[]) => any };

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 15, 2017

Member

We might also need methods?: ThisType<Methods & Computed & Pops & Data> to fully capture Vue's this instance.

I haven't try it myself, but will it cause cyclic reference?

@HerringtonDarkholme

HerringtonDarkholme Jun 15, 2017

Member

We might also need methods?: ThisType<Methods & Computed & Pops & Data> to fully capture Vue's this instance.

I haven't try it myself, but will it cause cyclic reference?

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

I haven't try it myself, but will it cause cyclic reference?

There are a few problems that potentially come up if you try to reference methods from data in which you'll have to have an explicit annotation on data(). But we can be prescriptive and tell people to just use functions when you need to operate on data in some way.

We might also need methods?: ThisType<Methods & Computed & Pops & Data> to fully capture Vue's this instance.

@HerringtonDarkholme that's not actually necessary. When a method needs its thistype, it will walk up each contextually typed object literal until it finds one that had a contextual type consisting of ThisType. If it does, it uses it. If not, it uses the innermost object literal's contextual type, and if there isn't one it just uses the type of the containing literal.

You should definitely try it out!

@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

I haven't try it myself, but will it cause cyclic reference?

There are a few problems that potentially come up if you try to reference methods from data in which you'll have to have an explicit annotation on data(). But we can be prescriptive and tell people to just use functions when you need to operate on data in some way.

We might also need methods?: ThisType<Methods & Computed & Pops & Data> to fully capture Vue's this instance.

@HerringtonDarkholme that's not actually necessary. When a method needs its thistype, it will walk up each contextually typed object literal until it finds one that had a contextual type consisting of ThisType. If it does, it uses it. If not, it uses the innermost object literal's contextual type, and if there isn't one it just uses the type of the containing literal.

You should definitely try it out!

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 15, 2017

Member

Tried it locally. It works like charm!

Sorry for the wrong reporting. I only tried accurateVueType in vetur, which seems to wrongly configured. I will investigate more in vetur.

Vetur's issue is caused by ComponentOption's type arg.

@HerringtonDarkholme

HerringtonDarkholme Jun 15, 2017

Member

Tried it locally. It works like charm!

Sorry for the wrong reporting. I only tried accurateVueType in vetur, which seems to wrongly configured. I will investigate more in vetur.

Vetur's issue is caused by ComponentOption's type arg.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

@yyx990803 the fact that ComponentOptions used to be generic on 1 type arg, and now has 4 unrelated type arguments, will cause some incompatibilities. That's the main one you'll run into with vue-class-component.

DanielRosenwasser commented Jun 15, 2017

@yyx990803 the fact that ComponentOptions used to be generic on 1 type arg, and now has 4 unrelated type arguments, will cause some incompatibilities. That's the main one you'll run into with vue-class-component.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

The build should now be fixed.

DanielRosenwasser commented Jun 15, 2017

The build should now be fixed.

@ktsn

This is awesome work! 👍
I badly wanted to use Vue.js with TypeScript on the native syntax 😄

BTW, we may also need to update vue-server-renderer types since it's depends on the core types.

Show outdated Hide outdated types/vue.d.ts
export type CombinedVueInstance<Data, Methods, Computed, Props> = Data & Methods & Computed & Props & Vue;
export type ExtendedVue<Constructor extends VueConstructor, Data, Methods, Computed, Props> =
(new (...args: any[]) => CombinedVueInstance<Data, Methods, Computed, Props>) &

This comment has been minimized.

@ktsn

ktsn Jun 15, 2017

Member

As ExtendedVue is a sub-class of the Vue constructor, we can expect the constructor signature would only receive one argument like below?

new (options: any) => CombinedVueInstance<Data, Methods, Computed, Props>

Not sure whether we could annotate the options with ThisTypedComponentOptionsWithArrayProps etc. since the type declaration might be so complicated 😂

@ktsn

ktsn Jun 15, 2017

Member

As ExtendedVue is a sub-class of the Vue constructor, we can expect the constructor signature would only receive one argument like below?

new (options: any) => CombinedVueInstance<Data, Methods, Computed, Props>

Not sure whether we could annotate the options with ThisTypedComponentOptionsWithArrayProps etc. since the type declaration might be so complicated 😂

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

As ExtendedVue is a sub-class of the Vue constructor, we can expect the constructor signature would only receive one argument like below?

@ktsn ExtendedVue creates an intersection with the mixin constructor (new (...args: any[]) => CombinedVueInstance<Data, Methods, Computed, Props>). This means it just absorbs the argument types of the Constructor type that's given. See Microsoft/TypeScript#13743 for more details

@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

As ExtendedVue is a sub-class of the Vue constructor, we can expect the constructor signature would only receive one argument like below?

@ktsn ExtendedVue creates an intersection with the mixin constructor (new (...args: any[]) => CombinedVueInstance<Data, Methods, Computed, Props>). This means it just absorbs the argument types of the Constructor type that's given. See Microsoft/TypeScript#13743 for more details

This comment has been minimized.

@ktsn

ktsn Jun 15, 2017

Member

Oh, I didn't understand about the mixin constructor correctly. Thank you for the explanation.

@ktsn

ktsn Jun 15, 2017

Member

Oh, I didn't understand about the mixin constructor correctly. Thank you for the explanation.

Show outdated Hide outdated types/vue.d.ts
extend<VC extends VueConstructor, PropNames extends string = never>(this: VC, definition: FunctionalComponentOptions<PropNames[], Record<PropNames, any>>): ExtendedVue<VC, {}, {}, {}, Record<PropNames, any>>;
extend<VC extends VueConstructor, Props extends Record<string, PropValidator>>(this: VC, definition: FunctionalComponentOptions<Props, Record<keyof Props, any>>): ExtendedVue<VC, {}, {}, {}, Record<keyof Props, any>>;
extend<VC extends VueConstructor, Data, Methods, Computed, PropNames extends string = never>(this: VC, options: ThisTypedComponentOptionsWithArrayProps<Data, Methods, Computed, PropNames>): ExtendedVue<VC, Data, Methods, Computed, Record<PropNames, any>>;
extend<VC extends VueConstructor, Data, Methods, Computed, Props extends Record<string, PropValidator>>(this: VC, options?: ThisTypedComponentOptionsWithRecordProps<Data, Methods, Computed, Props>): ExtendedVue<VC, Data, Methods, Computed, Record<keyof Props, any>>;

This comment has been minimized.

@ktsn

ktsn Jun 15, 2017

Member

In the following code, this type in Child component options does not seem to have parent's properties while the newed Child component has it.

const Parent = Vue.extend({
  data () {
    return { parent: 'Hello' }
  }
})

const Child = Parent.extend({
  methods: {
    foo () {
      this.parent // Compile error
    }
  }
})

const child = new Child()
child.parent // No error

But I'm not sure how we can solve this. It may need more type parameters to extract the parent instance types into ThisType of the child component options....

@ktsn

ktsn Jun 15, 2017

Member

In the following code, this type in Child component options does not seem to have parent's properties while the newed Child component has it.

const Parent = Vue.extend({
  data () {
    return { parent: 'Hello' }
  }
})

const Child = Parent.extend({
  methods: {
    foo () {
      this.parent // Compile error
    }
  }
})

const child = new Child()
child.parent // No error

But I'm not sure how we can solve this. It may need more type parameters to extract the parent instance types into ThisType of the child component options....

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

Good catch! I think I can fix this, but I'll have to look into it later today.

@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

Good catch! I think I can fix this, but I'll have to look into it later today.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

I just sent out a fix. In the previous iteration, extend inherited members on the static side instead of the instance side. I've now done the opposite. Give it another go - I've added the test that @ktsn mentioned above.

DanielRosenwasser commented Jun 15, 2017

I just sent out a fix. In the previous iteration, extend inherited members on the static side instead of the instance side. I've now done the opposite. Give it another go - I've added the test that @ktsn mentioned above.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jun 15, 2017

@yyx990803 I think I could undo the commit if you don't want to fix #5016 right away.

DanielRosenwasser commented Jun 15, 2017

@yyx990803 I think I could undo the commit if you don't want to fix #5016 right away.

@HerringtonDarkholme

Thanks again for the awesome work! It really push Vue's typing to the horizon of TypeScript!

Since this definition is very sophisticated, it is even nicer to capture compiling error in test as well as compiling success. So we can ensure our definition correctly reject wrong code. In TS repo we have baseline test. In other langauges, for example Shapeless, a type level library in Scala, have artifacts like macro to test compilation error.

Do you have any suggestion for library author to test compiling error?

children: VNode[];
slots(): any;
data: VNodeData;
parent: Vue;
injections: any
}
export type PropValidator = PropOptions | Constructor | Constructor[];

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

Do we have any chance to infer Prop type from Constructor? Something like the reverse of emitDecoratorMetadata

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

Do we have any chance to infer Prop type from Constructor? Something like the reverse of emitDecoratorMetadata

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

I originally tried to do that but the more I thought about it, the more time I realized it'd take to finish. I figured it'd be better to put the PR out and try that at a later stage.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

I originally tried to do that but the more I thought about it, the more time I realized it'd take to finish. I figured it'd be better to put the PR out and try that at a later stage.

/**
* This type should be used when an array of strings is used for a component's `props` value.
*/
export type ThisTypedComponentOptionsWithArrayProps<Instance extends Vue, Data, Methods, Computed, PropNames extends string> =

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

Can we re-export ThisTyped option in index? It is useful for library authors, I think.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

Can we re-export ThisTyped option in index? It is useful for library authors, I think.

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

Is that a great idea? Does re-exporting mean that this is committed to as part of the public interface?

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

Is that a great idea? Does re-exporting mean that this is committed to as part of the public interface?

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 17, 2017

Also, should I just rename this to ComponentOptionsWithArrayProps?

@DanielRosenwasser

DanielRosenwasser Jun 17, 2017

Also, should I just rename this to ComponentOptionsWithArrayProps?

readonly $slots: { [key: string]: VNode[] };
readonly $scopedSlots: { [key: string]: ScopedSlot };
readonly $isServer: boolean;
readonly $data: Record<string, any>;
readonly $props: Record<string, any>;

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

We can also make Vue generic so $data and $props can be typed. But I wonder if it is an overkill.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

We can also make Vue generic so $data and $props can be typed. But I wonder if it is an overkill.

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

I originally made it generic but ran into problems.

Namely, VueConstructor has two construct signatures. Each of those signatures has two different sets of type parameters. They each constructed, at least in part, a Vue<Data, Methods, Computed, Props>`, but each gave different type arguments.

When extending a Vue<Data, Methods, Computed, Props>, TypeScript tries to find the right construct signature, but it can't because it's not really possible to make a meaningful set of type arguments forVue` that would create the same output type for both signatures.

What it comes down to is that TypeScript has always had an assumption that the type parameters of a construct signature have had a direct correlation with the type parameters of the constructed type - but that's not entirely the case here. At least, that's my understanding of the problem.

So I could try to address this, but maybe down the line. Plus, does anyone need to access $data and $props outside of debugging?

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

I originally made it generic but ran into problems.

Namely, VueConstructor has two construct signatures. Each of those signatures has two different sets of type parameters. They each constructed, at least in part, a Vue<Data, Methods, Computed, Props>`, but each gave different type arguments.

When extending a Vue<Data, Methods, Computed, Props>, TypeScript tries to find the right construct signature, but it can't because it's not really possible to make a meaningful set of type arguments forVue` that would create the same output type for both signatures.

What it comes down to is that TypeScript has always had an assumption that the type parameters of a construct signature have had a direct correlation with the type parameters of the constructed type - but that's not entirely the case here. At least, that's my understanding of the problem.

So I could try to address this, but maybe down the line. Plus, does anyone need to access $data and $props outside of debugging?

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

Thanks for the detailed explanation!

Indeed, $data and $props are mainly for advanced usage like delegate / wrapper component (example). We would like to check template code in future. So a $props or $data might be helpful.

For ordinary users, Record should be enough.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

Thanks for the detailed explanation!

Indeed, $data and $props are mainly for advanced usage like delegate / wrapper component (example). We would like to check template code in future. So a $props or $data might be helpful.

For ordinary users, Record should be enough.

static config: {
use<T>(plugin: PluginObject<T> | PluginFunction<T>, options?: T): void;
mixin(mixin: typeof Vue | ComponentOptions<any, any, any, any>): void;

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

mixin does not have ThisTyped as component or extend, is this intended?

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

mixin does not have ThisTyped as component or extend, is this intended?

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

This is intended - I don't think ThisType would help the common scenarios, and I think don't think we can model the scenario all that well. For example, I don't think "get the type of all the methods from each mixin and intersect them` is possible at the moment.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

This is intended - I don't think ThisType would help the common scenarios, and I think don't think we can model the scenario all that well. For example, I don't think "get the type of all the methods from each mixin and intersect them` is possible at the moment.

*/
export type ThisTypedComponentOptionsWithArrayProps<Instance extends Vue, Data, Methods, Computed, PropNames extends string> =
object &
ComponentOptions<Data | ((this: Record<PropNames, any> & Instance) => Data), Methods, Computed, PropNames[]> &

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

👍 for only exposing prop!

@HerringtonDarkholme

HerringtonDarkholme Jun 16, 2017

Member

👍 for only exposing prop!

This comment has been minimized.

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

I agree! To be honest this was more a limitation of the type system but it honestly seemed like more of a feature than a bug. 😄

@DanielRosenwasser

DanielRosenwasser Jun 16, 2017

I agree! To be honest this was more a limitation of the type system but it honestly seemed like more of a feature than a bug. 😄

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jun 17, 2017

Do you have any suggestion for library author to test compiling error?

We have dtslint which has a testing facility to $ExpectType and $ExpectError. I could add some error tests there if you'd like. What did you have in mind?

DanielRosenwasser commented Jun 17, 2017

Do you have any suggestion for library author to test compiling error?

We have dtslint which has a testing facility to $ExpectType and $ExpectError. I could add some error tests there if you'd like. What did you have in mind?

@HerringtonDarkholme

This comment has been minimized.

Show comment
Hide comment
@HerringtonDarkholme

HerringtonDarkholme Jun 17, 2017

Member

@DanielRosenwasser Thanks for sharing dtslint! Definitely I will try to add it to Vue.

Since this pull request is already big, IMHO it is fine to separate dtslint to another PR.

Member

HerringtonDarkholme commented Jun 17, 2017

@DanielRosenwasser Thanks for sharing dtslint! Definitely I will try to add it to Vue.

Since this pull request is already big, IMHO it is fine to separate dtslint to another PR.

@ktsn

ktsn approved these changes Jun 17, 2017

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Jun 17, 2017

Member

@ktsn @HerringtonDarkholme seeing that both of you have approved this, I think the next step should be working out an upgrade guide - i.e. what would be different from the current recommendation.

@ktsn Does vue-class-component need to be upgraded to be compatible with the new types?

Member

yyx990803 commented Jun 17, 2017

@ktsn @HerringtonDarkholme seeing that both of you have approved this, I think the next step should be working out an upgrade guide - i.e. what would be different from the current recommendation.

@ktsn Does vue-class-component need to be upgraded to be compatible with the new types?

@octref

octref approved these changes Jun 17, 2017 edited

This looks great!
Hope later TypeScript-Vue-Starter can be updated too.

@ktsn

This comment has been minimized.

Show comment
Hide comment
@ktsn

ktsn Jun 17, 2017

Member

@yyx990803 I think we need to update vue-class-component since it uses ComponentOptions in its declarations.

vue-router and vuex need to be updated too as they augment the ComponentOptions type.

Member

ktsn commented Jun 17, 2017

@yyx990803 I think we need to update vue-class-component since it uses ComponentOptions in its declarations.

vue-router and vuex need to be updated too as they augment the ComponentOptions type.

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Jun 17, 2017

Member

Ok - I think I'll hold this off until:

  1. TS 2.4 is out
  2. vue-class-component has been updated.

This most likely means we will ship Vue 2.4 without the new types.

Member

yyx990803 commented Jun 17, 2017

Ok - I think I'll hold this off until:

  1. TS 2.4 is out
  2. vue-class-component has been updated.

This most likely means we will ship Vue 2.4 without the new types.

@alexdresko

This comment has been minimized.

Show comment
Hide comment
@alexdresko

alexdresko Jun 17, 2017

Okay, lemme put this out there, as I believe this is the correct thread for what I'm dealing with.

Right now, a basic reproduction of the Vue Getting Started doesn't work in TypeScript.

image

The only way to make that code work is to import Vue (I'm using this PR's definitions)

image

However, it doesn't appear that modules are a requirement for Vue, given I can do this in a basic HTML file:

image

We're a bit behind the times in some of our projects, though we do use TS. We just don't use modules. importing Vue forces TS to think we're using modules, and that breaks a lot of our existing code.

Are the definitions at fault here, or my general lack of understanding otherwise?

If it's the definitions, maybe that's something you can take into consideration.

alexdresko commented Jun 17, 2017

Okay, lemme put this out there, as I believe this is the correct thread for what I'm dealing with.

Right now, a basic reproduction of the Vue Getting Started doesn't work in TypeScript.

image

The only way to make that code work is to import Vue (I'm using this PR's definitions)

image

However, it doesn't appear that modules are a requirement for Vue, given I can do this in a basic HTML file:

image

We're a bit behind the times in some of our projects, though we do use TS. We just don't use modules. importing Vue forces TS to think we're using modules, and that breaks a lot of our existing code.

Are the definitions at fault here, or my general lack of understanding otherwise?

If it's the definitions, maybe that's something you can take into consideration.

@prashantkhurana

This comment has been minimized.

Show comment
Hide comment
@prashantkhurana

prashantkhurana Jul 1, 2017

Once merged, will this allow us to type the props also i.e. props implementing an interface? Run time checking would be nice, but I am more interested in the code completion, compile error etc the ide will provide once the props are typed.

prashantkhurana commented Jul 1, 2017

Once merged, will this allow us to type the props also i.e. props implementing an interface? Run time checking would be nice, but I am more interested in the code completion, compile error etc the ide will provide once the props are typed.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Jul 1, 2017

@prashantkhurana unfortunately not, but I can try to revisit this. You'll at least have TypeScript aware of your props though.

DanielRosenwasser commented Jul 1, 2017

@prashantkhurana unfortunately not, but I can try to revisit this. You'll at least have TypeScript aware of your props though.

@prashantkhurana

This comment has been minimized.

Show comment
Hide comment
@prashantkhurana

prashantkhurana Jul 1, 2017

thanks @DanielRosenwasser . yeah that would be the icing on the cake:)

prashantkhurana commented Jul 1, 2017

thanks @DanielRosenwasser . yeah that would be the icing on the cake:)

@kaorun343

This comment has been minimized.

Show comment
Hide comment
@kaorun343

kaorun343 Jul 3, 2017

Contributor

Hi.

I just wondered how to add Dom references to $refs.
Current definitions allow us to write like this, but the future version won't allow to do so.

interface App extends Vue {
  $refs: {
    target: HTMLElement
  }
}

export default {
  created() {
    this.$refs.target
  }
} as ComponentOptions<App>

If many people doesn't care about this, I think there's no need to fix this PR, but if somebody takes care about this, it should be done so.
For me, I haven't written like this yet, so I don't care about it...
How do you think about this?

Contributor

kaorun343 commented Jul 3, 2017

Hi.

I just wondered how to add Dom references to $refs.
Current definitions allow us to write like this, but the future version won't allow to do so.

interface App extends Vue {
  $refs: {
    target: HTMLElement
  }
}

export default {
  created() {
    this.$refs.target
  }
} as ComponentOptions<App>

If many people doesn't care about this, I think there's no need to fix this PR, but if somebody takes care about this, it should be done so.
For me, I haven't written like this yet, so I don't care about it...
How do you think about this?

@HerringtonDarkholme

This comment has been minimized.

Show comment
Hide comment
@HerringtonDarkholme

HerringtonDarkholme Jul 3, 2017

Member

@kaorun343 I have come up with this:

Rather than keeping ComponentOption as an interface, making it a type alias seems better choice. With the aid of default generic argument, we can even reduce the impact of breaking change.

Example:

type ComponentOption<Vue, D={}, M={}, C={}, P={}> = InternalOption<D,M,C,P> & ThisType<D&M&C&P&Vue>

interface InternalOption<Data, Methods, Computed, Props> {
    methods?: Methods
}

interface App {
    $refs: {
        target: HTMLInputElement
    }
}

var a: ComponentOption<App> = { // no breaking change
    methods: {
        test() {
            this.$refs.target
        }
    }
}

interface MyComponentOption extends ComponentOption<App, {foo: number}> { // refine more
}

@DanielRosenwasser what's your opinion?

It seems without Microsoft/TypeScript#14400 optional generic type inference, users will have to annotate more.

Member

HerringtonDarkholme commented Jul 3, 2017

@kaorun343 I have come up with this:

Rather than keeping ComponentOption as an interface, making it a type alias seems better choice. With the aid of default generic argument, we can even reduce the impact of breaking change.

Example:

type ComponentOption<Vue, D={}, M={}, C={}, P={}> = InternalOption<D,M,C,P> & ThisType<D&M&C&P&Vue>

interface InternalOption<Data, Methods, Computed, Props> {
    methods?: Methods
}

interface App {
    $refs: {
        target: HTMLInputElement
    }
}

var a: ComponentOption<App> = { // no breaking change
    methods: {
        test() {
            this.$refs.target
        }
    }
}

interface MyComponentOption extends ComponentOption<App, {foo: number}> { // refine more
}

@DanielRosenwasser what's your opinion?

It seems without Microsoft/TypeScript#14400 optional generic type inference, users will have to annotate more.

@HerringtonDarkholme

This comment has been minimized.

Show comment
Hide comment
@HerringtonDarkholme

HerringtonDarkholme Jul 5, 2017

Member

One major issue is typed prop declaration:

var a = new Vue({
    props: {
        someProp: {
            type: String
        }
    },
    methods: {
        test() {
            this.someProp.toLowerCase() // error
        }
    }
})

Another issue is still how to refine $refs, $el and friends. Ideally, if we can support such usage

interface App {
    $refs: {
        target: HTMLInputElement
    }
}

new Vue<App>({  // we only provide the first type argument, other type arguments are inferred.
  methods: {
     setVal() {
       this.$refs.target.value = this.someProp
    }
   },
  props: ['someProp']
})

However, such API requires optional generic type inference as suggested in Microsoft/TypeScript#14400

For now, we can still roll out own helper function to refine specific fields which uses ThisTypedComponentOptionsWithRecordProps

Member

HerringtonDarkholme commented Jul 5, 2017

One major issue is typed prop declaration:

var a = new Vue({
    props: {
        someProp: {
            type: String
        }
    },
    methods: {
        test() {
            this.someProp.toLowerCase() // error
        }
    }
})

Another issue is still how to refine $refs, $el and friends. Ideally, if we can support such usage

interface App {
    $refs: {
        target: HTMLInputElement
    }
}

new Vue<App>({  // we only provide the first type argument, other type arguments are inferred.
  methods: {
     setVal() {
       this.$refs.target.value = this.someProp
    }
   },
  props: ['someProp']
})

However, such API requires optional generic type inference as suggested in Microsoft/TypeScript#14400

For now, we can still roll out own helper function to refine specific fields which uses ThisTypedComponentOptionsWithRecordProps

@prashantkhurana

This comment has been minimized.

Show comment
Hide comment
@prashantkhurana

prashantkhurana Jul 10, 2017

@HerringtonDarkholme I have been thinking about prop typing a bit. One (not so elegant maybe, but quick) way to do prop typing is inferring the types from the default value of the prop.

image

Another is to from the type field but that required me to do ugly casting.

image

prashantkhurana commented Jul 10, 2017

@HerringtonDarkholme I have been thinking about prop typing a bit. One (not so elegant maybe, but quick) way to do prop typing is inferring the types from the default value of the prop.

image

Another is to from the type field but that required me to do ugly casting.

image

Show outdated Hide outdated types/options.d.ts
export type ThisTypedComponentOptionsWithRecordProps<Instance extends Vue, Data, Methods, Computed, Props> =
object &
ComponentOptions<Data | ((this: Record<keyof Props, any> & Instance) => Data), Methods, Computed, Props> &
ThisType<CombinedVueInstance<Instance, Data, Methods, Computed, Props>>;

This comment has been minimized.

@HerringtonDarkholme

HerringtonDarkholme Jul 10, 2017

Member

Here Props should be Record<keyof Props, any>, right?

@HerringtonDarkholme

HerringtonDarkholme Jul 10, 2017

Member

Here Props should be Record<keyof Props, any>, right?

[release] weex-vue-framework@2.4.2-weex.1 (#6196)
* build(release weex): ignore the file path of entries

* [release] weex-vue-framework@2.4.2-weex.1
@championswimmer

This comment has been minimized.

Show comment
Hide comment
@championswimmer

championswimmer Jul 27, 2017

What's the status with this ?

I pulled it locally, and tried it out in my project, and everything seems to work pretty well. Almost negates the need of vue-class-component

championswimmer commented Jul 27, 2017

What's the status with this ?

I pulled it locally, and tried it out in my project, and everything seems to work pretty well. Almost negates the need of vue-class-component

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Aug 1, 2017

@HerringtonDarkholme the problem with your suggestion is that it still breaks people who've done module augmentations (because you're moving from an interface to a type alias) but I think that would potentially be okay. It's just something to keep in mind.

Would it be okay to pull this in and then revisit that change instead? It might be easier for the core team to gauge the impact of that.

DanielRosenwasser commented Aug 1, 2017

@HerringtonDarkholme the problem with your suggestion is that it still breaks people who've done module augmentations (because you're moving from an interface to a type alias) but I think that would potentially be okay. It's just something to keep in mind.

Would it be okay to pull this in and then revisit that change instead? It might be easier for the core team to gauge the impact of that.

Show outdated Hide outdated package.json
@@ -118,7 +118,7 @@
"selenium-server": "^2.53.1",
"serialize-javascript": "^1.3.0",
"shelljs": "^0.7.8",
"typescript": "^2.3.4",
"typescript": "2.5.0-dev.20170615",

This comment has been minimized.

@znck

znck Aug 2, 2017

Member

Shouldn't it be depending on a stable version?

@znck

znck Aug 2, 2017

Member

Shouldn't it be depending on a stable version?

This comment has been minimized.

@nickmessing

nickmessing Aug 2, 2017

Member

It depended on some experimental changes but afaik they landed in 2.4, didn't they @DanielRosenwasser?

@nickmessing

nickmessing Aug 2, 2017

Member

It depended on some experimental changes but afaik they landed in 2.4, didn't they @DanielRosenwasser?

@HerringtonDarkholme

This comment has been minimized.

Show comment
Hide comment
@HerringtonDarkholme

HerringtonDarkholme Aug 2, 2017

Member

@DanielRosenwasser Indeed.

After several trials, I think changing to interface ComponentOptions<V, D=any, M=any, C=any, P=any> might introduce fewer upgrading issues.

Member

HerringtonDarkholme commented Aug 2, 2017

@DanielRosenwasser Indeed.

After several trials, I think changing to interface ComponentOptions<V, D=any, M=any, C=any, P=any> might introduce fewer upgrading issues.

@blake-newman

This comment has been minimized.

Show comment
Hide comment
@blake-newman

blake-newman Aug 15, 2017

Member

@HerringtonDarkholme @DanielRosenwasser Anything that still needs to be done, can i help out with anything?

Member

blake-newman commented Aug 15, 2017

@HerringtonDarkholme @DanielRosenwasser Anything that still needs to be done, can i help out with anything?

@HerringtonDarkholme

This comment has been minimized.

Show comment
Hide comment
@HerringtonDarkholme

HerringtonDarkholme Aug 15, 2017

Member

@blake-newman I think this pull request needs update dependency and tweak Props option type.

@DanielRosenwasser If you're busy with other things, I'm glad to take this over.

Member

HerringtonDarkholme commented Aug 15, 2017

@blake-newman I think this pull request needs update dependency and tweak Props option type.

@DanielRosenwasser If you're busy with other things, I'm glad to take this over.

@AlexStacker

This comment has been minimized.

Show comment
Hide comment
@AlexStacker

AlexStacker Aug 15, 2017

Hi, @DanielRosenwasser When I clone your branch vue-router,It seems not work. It took me a day to find out why.Fortunately, I found it.

This is not sure of the return value。
typescript_2
typescript_3

This is the right to return to rewrite.
typescript_1
typescript
I just started to learn Vue recently, do not know to rewrite the right.
That branch, I can't open an issue, and that's relevant, so I submitted it here.

AlexStacker commented Aug 15, 2017

Hi, @DanielRosenwasser When I clone your branch vue-router,It seems not work. It took me a day to find out why.Fortunately, I found it.

This is not sure of the return value。
typescript_2
typescript_3

This is the right to return to rewrite.
typescript_1
typescript
I just started to learn Vue recently, do not know to rewrite the right.
That branch, I can't open an issue, and that's relevant, so I submitted it here.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Aug 16, 2017

Just pushed a few new commits, but not sure why the build is failing.

SUMMARY:
✔ 969 tests completed
✖ 1 test failed

FAILED TESTS:
  Transition mode
    ✖ transition out-in on async component (resolve after leave complete)
      PhantomJS 2.1.1 (Linux 0.0.0)
    Expected 1 to be 0.
    webpack:///test/unit/features/transition/transition-mode.spec.js:480:44 <- index.js:149624:44
    shift@webpack:///test/helpers/wait-for-update.js:24:32 <- index.js:90813:36

DanielRosenwasser commented Aug 16, 2017

Just pushed a few new commits, but not sure why the build is failing.

SUMMARY:
✔ 969 tests completed
✖ 1 test failed

FAILED TESTS:
  Transition mode
    ✖ transition out-in on async component (resolve after leave complete)
      PhantomJS 2.1.1 (Linux 0.0.0)
    Expected 1 to be 0.
    webpack:///test/unit/features/transition/transition-mode.spec.js:480:44 <- index.js:149624:44
    shift@webpack:///test/helpers/wait-for-update.js:24:32 <- index.js:90813:36
@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Aug 16, 2017

Just amended and re-pushed. Looks like there is a flaky test.

DanielRosenwasser commented Aug 16, 2017

Just amended and re-pushed. Looks like there is a flaky test.

@DanielRosenwasser

This comment has been minimized.

Show comment
Hide comment
@DanielRosenwasser

DanielRosenwasser Aug 16, 2017

@HerringtonDarkholme if you can merge and take over, that would be absolutely fantastic. I'd be glad to help however I can.

DanielRosenwasser commented Aug 16, 2017

@HerringtonDarkholme if you can merge and take over, that would be absolutely fantastic. I'd be glad to help however I can.

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Aug 29, 2017

Member

Closing with the work being continued in #6391

Member

yyx990803 commented Aug 29, 2017

Closing with the work being continued in #6391

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment