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

createComponent in TypeScript not inherit from Vue ComponentOptions #63

Closed
TrungRueta opened this issue Aug 16, 2019 · 11 comments
Closed

Comments

@TrungRueta
Copy link

from Vue 2.x , when use with TypeScript we always write shims file d.ts whichs support static type property used inside vue component object.
ex: in Nuxt, each component use as page need defined $options.layout = 'layout name' like this:

{
    name: 'page',
    layout: 'myLayout'
}

To typesafe this custom property, they provide d.ts:

// ComponentOptions is declared in types/options.d.ts
declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    layout?: string
  }
}

For now vue-function-api provide function createComponent to help TS's developer can inherit typesafe from package. View d.ts file i see function defined:

export declare function createComponent<PropsOptions, RawBindings>(options: ComponentOptions<PropsOptions, RawBindings>): VueProxy<PropsOptions, RawBindings>;

export interface ComponentOptions<PropsOptions = ComponentPropsOptions, RawBindings = Data, Props = ExtractPropTypes<PropsOptions>> {
    props?: PropsOptions;
    setup?: SetupFunction<Props, RawBindings>;
}

This not inherit from Vue ComponentOptions , so we lost all typesafe from shims file.

A modify d.ts code will help a lot to inherit from Vue 2.x setup env .
Thankyou!

@TrungRueta
Copy link
Author

ps: After more research out package, i found that in index.d.ts file we had modify Vue Component Options really;

vue-function-api/dist/index.d.ts:

import Vue, { VueConstructor } from 'vue';
import { SetupFunction, SetupContext } from './ts-api';
import { Wrapper } from './wrappers';
declare module 'vue/types/options' {
    interface ComponentOptions<V extends Vue> {
        setup?: SetupFunction<{}, {}>;
    }
}
....

I dont know why we had define setup function into Vue Component Options but not use it when define createComponent function, but use custom interface which lack of inherit from Vue. But from this point i had work around for component:

shims-vue.d.ts

// ComponentOptions is declared in types/options.d.ts
declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    myOption?: string;
  }
}
import Vue, { ComponentOptions } from 'vue';
import { createComponent } from 'vue-function-api';
import { ComponentPropsOptions } from 'vue-function-api/dist/ts-api/componentProps';

export default createComponent({
  setup(props, context) {
    return {};
  },
  myOption: 'yes it work'
} as ComponentOptions<Vue>);

Hope this will help we fix issue faster. Thank again for your good stuff before new age of Vue 3 💃

@PatrykWalach
Copy link

If you use as assignment wrapping the code in createComponent does nothing.
If you want to use createComponent you have to extend the interface used by the function as an argument.

import { ComponentOptions } from 'vue-function-api/dist/ts-api/component'

declare module 'vue-function-api/dist/ts-api/component' {
  interface ComponentOptions {
    myOption?: string
  }
}

@IlCallo
Copy link

IlCallo commented Aug 16, 2019

It seems that not being an instance of Vue anymore, but a VueProxy instead, this change broke most testing methods (eg. shallowMount(), mount()) as they cannot infer wrapper.vm type.
This results into either a TS error when mounting or the vm type to be never, making it useless.

If the component doesn't have a props property, its instance will be wrongly inferred as a FunctionalComponent and TypeScript will fire off an error asking for the missing functional property.

@TrungRueta
Copy link
Author

If you use as assignment wrapping the code in createComponent does nothing.
If you want to use createComponent you have to extend the interface used by the function as an argument.

import { ComponentOptions } from 'vue-function-api/dist/ts-api/component'

declare module 'vue-function-api/dist/ts-api/component' {
  interface ComponentOptions {
    myOption?: string
  }
}

i have update version of @PatrykWalach :

import { ComponentOptions } from 'vue-function-api/dist/ts-api/component';
import Vue, { ComponentOptions as VueComponentOptions } from 'vue';

declare module 'vue-function-api/dist/ts-api/component' {
  interface ComponentOptions extends VueComponentOptions<Vue> {}
}

like this we will have all properties typesafe from vue constructor 2.x. and still allow Infer prop parameter of setup.

in case you dont want input all properties, can do:

import { ComponentOptions } from 'vue-function-api/dist/ts-api/component';
import Vue, { ComponentOptions as VueComponentOptions } from 'vue';

declare module 'vue-function-api/dist/ts-api/component' {
  interface ComponentOptions extends Pick<VueComponentOptions<Vue>, 'name' | 'components' |....> {
  }
}

@IlCallo
Copy link

IlCallo commented Aug 20, 2019

Shouldn't it be

import Vue, { ComponentOptions as VueComponentOptions } from 'vue';
declare module 'vue-function-api/dist/ts-api/component' {
  interface ComponentOptions
    extends Exclude<VueComponentOptions<Vue>, 'props'> {}
}

?

Otherwise you're overwriting props typings with their v2 counterparts.

@TrungRueta
Copy link
Author

@IlCallo hm... this depend on how many property you need ship from vue 2.x into vue-function-api typechecking. Actually i not need all of them , only need name, components to make Vue2.x system work.

@IlCallo
Copy link

IlCallo commented Aug 21, 2019

Yeah, I'm talking about the first mode you used, which adds everything.
Even doing as I did (leaving off props from original type) rises a warning in TS I actually don't understand.

Interface 'ComponentOptions<PropsOptions, RawBindings, Props>' incorrectly extends interface 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<string, any>>'.
  Types of property 'props' are incompatible.
    Type 'PropsOptions | undefined' is not assignable to type 'string[] | RecordPropsDefinition<Record<string, any>> | undefined'.
      Type 'PropsOptions' is not assignable to type 'string[] | RecordPropsDefinition<Record<string, any>> | undefined'.
        Type 'PropsOptions' is not assignable to type 'string[]'.

Also, import { ComponentOptions } from 'vue-function-api/dist/ts-api/component'; doesn't appear to do anything, what's that import for?

@TrungRueta
Copy link
Author

@IlCallo i see, you right my first trick will raise issue with typechecking, in this case my only way i think good is manual transfer properties from Vue 2.x we really need into Vue3.x type:

import { ComponentOptions } from 'vue-function-api/dist/ts-api/component';
import Vue, { ComponentOptions as VueComponentOptions } from 'vue';

declare module 'vue-function-api/dist/ts-api/component' {
  type d = VueComponentOptions<Vue>;

  interface ComponentOptions {
    name?: d['name'];
    components?: d['components'];
    methods?: d['methods'];
  }
}

This is my d.ts file , i only need methods , components, name for basic vue component.

also about question what the import api component line doing: I think it is for reference to ComponentOptions of vue-function-api, no ?

this is file d.ts to extend ComponentOptions of vue , i pick from Vue TS document and Nuxt project:

import Vue, { ComponentOptions } from 'vue';
import { VeeValidateComponentOptions } from 'vee-validate';

declare module 'vue/types/vue' {
  // Global properties can be declared
  // on the `VueConstructor` interface
  interface VueConstructor {}
}

// ComponentOptions is declared in types/options.d.ts
declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    $_veeValidate?: VeeValidateComponentOptions;
  }
}

my project i store both 2 d.ts file, one for extend for custom option, buefy type etc etc into v2.x Vue ComponentOptions, like normal, and second file is extends what property i want into vue-function-api's ComponentOptions . with my last suggest trick above i nolonger meet typechecking fails anymore. can you try .

@hzgotb
Copy link

hzgotb commented Aug 21, 2019

WX20190821-220125@2x

vue-router error.
import SignIn from './views/SignIn.vue';

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/sign-in',
      name: 'SignIn',
      component: SignIn,   // use createComponent
    },
  ],
});

image

@IlCallo
Copy link

IlCallo commented Aug 21, 2019

also about question what the import api component line doing: I think it is for reference to ComponentOptions of vue-function-api, no ?

You actually don't need it. You are defining in the vue-function-api namespace a new interface with the same name of the old one and they are merged because of TypeScript interface merging capabilities. Importing it won't do anything because you are not actually referencing it.

with my last suggest trick above i nolonger meet typechecking fails anymore. can you try .

Will check it next week, tomorrow I go on vacation :P
I already solved using your second way of just selecting the needed properties, anyway. I needed just name and components too.

@manuelojeda
Copy link

WX20190821-220125@2x vue-router error.
import SignIn from './views/SignIn.vue';

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/sign-in',
      name: 'SignIn',
      component: SignIn,   // use createComponent
    },
  ],
});

image

Actually the fix to this is pretty simple, just they haven't add it to the VueCLI, you need to add a type into the Route Array, you can check this:
vuejs/vue-cli#4805

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants