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

Will Volar consider supporting Component Data interface like vetur for users to configure code hints? #418

Closed
wjdnb opened this issue Aug 25, 2021 · 16 comments
Labels
question Further information is requested

Comments

@wjdnb
Copy link

wjdnb commented Aug 25, 2021

https://vuejs.github.io/vetur/guide/component-data.html

Will Volar consider supporting this feature?

@johnsoncodehk
Copy link
Member

No, this is one of the features that Volar deliberately avoids. It will force the third-party librarys to write specific files in order to support specific IDE plugins.

If you write your components with lang="ts", you will get native TS hints of components without additional configuration.

@johnsoncodehk johnsoncodehk added the question Further information is requested label Aug 25, 2021
@philfontaine
Copy link

philfontaine commented Aug 30, 2021

@johnsoncodehk What exactly is your take/solution for all the people who are using UI libraries like Vuetify, Quasar, etc. ? When using these libraries, the normal approach is to let these libraries auto import these components for you, so that you can write a bunch of <q-btn>, <q-input> etc. without having to import them in every single component you write.

I understand you made that choice for a reason, but without a solution it makes switching to volar a no-go.

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Aug 30, 2021

@philfontaine Are you talking about the definition of global components?

If the library defines the GlobalComponents interface, users can directly get tag prompts like <q-btn>, <q-input>. (May also need to set types option of tsconfig)

If the library does not have a GlobalComponents interface, but provides a type definition of components, you can define the GlobalComponents interface yourself.

See https://github.com/johnsoncodehk/volar#using.

@soulsam480
Copy link

@philfontaine Hi, were you able to make it work with quasar ?

@philfontaine
Copy link

Nope, I got busy and I didn't really try

@soulsam480
Copy link

soulsam480 commented Sep 9, 2021

@philfontaine sorry, figured it out. For anyone coming here for quasar paste this in component.d.ts

type OmitDefaults<T extends any> = Pick<T, Filter<keyof T, '$'>>;

/* eslint-disable @typescript-eslint/no-unused-vars */
type Filter<Set, Needle extends string> = Set extends `${Needle}${infer _X}` ? never : Set;

declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    QAjaxBar: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QAjaxBar']>>>;

    QAvatar: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QAvatar']>>>;

    QBadge: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QBadge']>>>;

    QBanner: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QBanner']>>>;

    QBar: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QBar']>>>;

    QBreadcrumbs: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QBreadcrumbs']>>
    >;

    QBreadcrumbsEl: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QBreadcrumbsEl']>>
    >;

    QBtnDropdown: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QBtnDropdown']>>
    >;

    QBtnGroup: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QBtnGroup']>>>;

    QBtnToggle: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QBtnToggle']>>>;

    QBtn: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QBtn']>>>;

    QCard: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QCard']>>>;

    QCardActions: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QCardActions']>>
    >;

    QCardSection: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QCardSection']>>
    >;

    QCarousel: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QCarousel']>>>;

    QCarouselControl: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QCarouselControl']>>
    >;

    QCarouselSlide: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QCarouselSlide']>>
    >;

    QChatMessage: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QChatMessage']>>
    >;

    QCheckbox: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QCheckbox']>>>;

    QChip: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QChip']>>>;

    QCircularProgress: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QCircularProgress']>>
    >;

    QColor: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QColor']>>>;

    QDate: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QDate']>>>;

    QDialog: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QDialog']>>>;

    QDrawer: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QDrawer']>>>;

    QEditor: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QEditor']>>>;

    QExpansionItem: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QExpansionItem']>>
    >;

    QFab: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QFab']>>>;

    QFabAction: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QFabAction']>>>;

    QField: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QField']>>>;

    QFile: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QFile']>>>;

    QFooter: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QFooter']>>>;

    QForm: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QForm']>>>;

    QFormChildMixin: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QFormChildMixin']>>
    >;

    QHeader: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QHeader']>>>;

    QIcon: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QIcon']>>>;

    QImg: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QImg']>>>;

    QInfiniteScroll: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QInfiniteScroll']>>
    >;

    QInnerLoading: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QInnerLoading']>>
    >;

    QInput: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QInput']>>>;

    QIntersection: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QIntersection']>>
    >;

    QItem: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QItem']>>>;

    QItemLabel: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QItemLabel']>>>;

    QItemSection: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QItemSection']>>
    >;

    QList: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QList']>>>;

    QKnob: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QKnob']>>>;

    QLayout: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QLayout']>>>;

    QLinearProgress: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QLinearProgress']>>
    >;

    QMarkupTable: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QMarkupTable']>>
    >;

    QMenu: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QMenu']>>>;

    QNoSsr: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QNoSsr']>>>;

    QOptionGroup: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QOptionGroup']>>
    >;

    QPageScroller: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QPageScroller']>>
    >;

    QPageSticky: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QPageSticky']>>
    >;

    QPage: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QPage']>>>;

    QPageContainer: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QPageContainer']>>
    >;

    QPagination: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QPagination']>>
    >;

    QParallax: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QParallax']>>>;

    QPopupEdit: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QPopupEdit']>>>;

    QPopupProxy: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QPopupProxy']>>
    >;

    QPullToRefresh: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QPullToRefresh']>>
    >;

    QRadio: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QRadio']>>>;

    QRange: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QRange']>>>;

    QRating: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QRating']>>>;

    QResizeObserver: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QResizeObserver']>>
    >;

    QResponsive: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QResponsive']>>
    >;

    QScrollArea: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QScrollArea']>>
    >;

    QScrollObserver: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QScrollObserver']>>
    >;

    QSelect: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSelect']>>>;

    QSeparator: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSeparator']>>>;

    QSkeleton: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSkeleton']>>>;

    QSlideItem: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSlideItem']>>>;

    QSlideTransition: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSlideTransition']>>
    >;

    QSlider: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSlider']>>>;

    QSpace: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSpace']>>>;

    QSpinner: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSpinner']>>>;

    QSpinnerAudio: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerAudio']>>
    >;

    QSpinnerBall: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerBall']>>
    >;

    QSpinnerBars: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerBars']>>
    >;

    QSpinnerBox: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerBox']>>
    >;

    QSpinnerClock: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerClock']>>
    >;

    QSpinnerComment: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerComment']>>
    >;

    QSpinnerCube: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerCube']>>
    >;

    QSpinnerDots: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerDots']>>
    >;

    QSpinnerFacebook: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerFacebook']>>
    >;

    QSpinnerGears: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerGears']>>
    >;

    QSpinnerGrid: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerGrid']>>
    >;

    QSpinnerHearts: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerHearts']>>
    >;

    QSpinnerHourglass: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerHourglass']>>
    >;

    QSpinnerInfinity: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerInfinity']>>
    >;

    QSpinnerIos: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerIos']>>
    >;

    QSpinnerOrbit: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerOrbit']>>
    >;

    QSpinnerOval: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerOval']>>
    >;

    QSpinnerPie: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerPie']>>
    >;

    QSpinnerPuff: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerPuff']>>
    >;

    QSpinnerRadio: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerRadio']>>
    >;

    QSpinnerRings: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerRings']>>
    >;

    QSpinnerTail: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QSpinnerTail']>>
    >;

    QSplitter: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QSplitter']>>>;

    QStep: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QStep']>>>;

    QStepper: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QStepper']>>>;

    QStepperNavigation: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QStepperNavigation']>>
    >;

    QTabPanel: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTabPanel']>>>;

    QTabPanels: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTabPanels']>>>;

    QTable: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTable']>>>;

    QTd: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTd']>>>;

    QTh: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTh']>>>;

    QTr: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTr']>>>;

    QRouteTab: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QRouteTab']>>>;

    QTab: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTab']>>>;

    QTabs: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTabs']>>>;

    QTime: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTime']>>>;

    QTimeline: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTimeline']>>>;

    QTimelineEntry: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QTimelineEntry']>>
    >;

    QToggle: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QToggle']>>>;

    QToolbar: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QToolbar']>>>;

    QToolbarTitle: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QToolbarTitle']>>
    >;

    QTooltip: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTooltip']>>>;

    QTree: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QTree']>>>;

    QUploader: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QUploader']>>>;

    QUploaderAddTrigger: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QUploaderAddTrigger']>>
    >;

    QVideo: DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['QVideo']>>>;

    QVirtualScroll: DefineComponent<
      OmitDefaults<InstanceType<typeof import('quasar')['QVirtualScroll']>>
    >;
  }
}

export {};

a small script for this is

try {
// s is a string of all quasar components separated by newline
  const components = s
    .split('\n')
    .reduce((arr, component) => {
      if (!component) return arr;
      console.log('extracting', component);

      arr.push(`
     ${component} : DefineComponent<OmitDefaults<InstanceType<typeof import('quasar')['${component}']>>>;
    `);
      return arr;
    }, [])
    .join('\n');

  const built = `

  type OmitDefaults<T extends any> = Pick<T, Filter<keyof T, '$'>>;

  type Filter<Set, Needle extends string> = Set extends \`$\{Needle\}\$\{infer _X\}\` ? never : Set;

  declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    ${components}
  }
}

export {};
`;

  writeFileSync('./src/components.d.ts', built);
} catch (error) {
  console.log(error);
}

@johnsoncodehk
Copy link
Member

@soulsam480 thanks for the share! But it seem component props type-checking / intellisense not work in this definition, I think there is still room for improvement.

@soulsam480
Copy link

@soulsam480 thanks for the share! But it seem component props type-checking / intellisense not work in this definition, I think there is still room for improvement.

Hi, yes that's true. The quasar team is working on supporting volar natively with proper ts definitions for components. This is just a workaround to achieve manageable autocompletion.

@johnsoncodehk
Copy link
Member

The component type is not completely correct, but some props type-checking / auto-complete is working.

螢幕截圖 2021-09-14 上午1 54 46

import { ComponentConstructor } from 'quasar';

type Quasar = typeof import('quasar');
type QuasarComponentNames = keyof { [K in keyof Quasar as (Quasar)[K] extends ComponentConstructor<infer _> ? K : never]: any };
type QuasarComponents = Pick<Quasar, QuasarComponentNames>;
type QuasarFuncationalComponents = { [K in keyof QuasarComponents]: Quasar[K] extends ComponentConstructor<infer C> ? (props: Partial<C>) => void : never };

declare module '@vue/runtime-core' {
	export interface GlobalComponents extends QuasarFuncationalComponents { }
}

@soulsam480
Copy link

The component type is not completely correct, but some props type-checking / auto-complete is working.

螢幕截圖 2021-09-14 上午1 54 46
import { ComponentConstructor } from 'quasar';

type Quasar = typeof import('quasar');
type QuasarComponentNames = keyof { [K in keyof Quasar as (Quasar)[K] extends ComponentConstructor<infer _> ? K : never]: any };
type QuasarComponents = Pick<Quasar, QuasarComponentNames>;
type QuasarFuncationalComponents = { [K in keyof QuasarComponents]: Quasar[K] extends ComponentConstructor<infer C> ? (props: Partial<C>) => void : never };

declare module '@vue/runtime-core' {
	export interface GlobalComponents extends QuasarFuncationalComponents { }
}

I know official support is coming but this is an error that I get from the above approach

'__VLS_31' cannot be used as a JSX component.
  Its return type 'void' is not a valid JSX element.ts(2786)
(property) 'q-option-group': (props: Partial<QOptionGroup>) => void

@johnsoncodehk
Copy link
Member

@soulsam480 it should be fixed in 0.27.29. (#553)

@hinogi
Copy link

hinogi commented Oct 7, 2021

@johnsoncodehk Doesn't seem to be the case

0.28.0
image

@johnsoncodehk
Copy link
Member

@hinogi yes I make something wrong :S, you can change (props: Partial<C>) => void to (props: Partial<C>) => any to fix.

@soulsam480
Copy link

In the latest quasar release, the types are now available and work great.

@douglasg14b
Copy link

douglasg14b commented Mar 8, 2022

The example here: https://github.com/johnsoncodehk/volar/tree/master/extensions/vscode-vue-language-features

Doesn't seem to work in Vue2?

// components.d.ts
declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
  }
}

export {}

I still get no autocomplete or API info for router links or views.

How is this supposed to work for larger frameworks like Vuetify in Vue 2? Is additional configuration required outside of the above?

@q1uxu
Copy link

q1uxu commented Oct 10, 2022

The example here: https://github.com/johnsoncodehk/volar/tree/master/extensions/vscode-vue-language-features

Doesn't seem to work in Vue2?

// components.d.ts
declare module '@vue/runtime-core' {
  export interface GlobalComponents {
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
  }
}

export {}

I still get no autocomplete or API info for router links or views.

How is this supposed to work for larger frameworks like Vuetify in Vue 2? Is additional configuration required outside of the above?

The config of vue2.x is different from vue3.x.

// components.d.ts
declare module '@vue/runtime-core' {  // Vue 3
// declare module 'vue' {   // Vue 2.7
// declare module '@vue/runtime-dom' {  // Vue <= 2.6.14
  export interface GlobalComponents {
    RouterLink: typeof import('vue-router')['RouterLink']
    RouterView: typeof import('vue-router')['RouterView']
  }
}

export {}

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

No branches or pull requests

7 participants