From 4b6a504d66c5dc0f98831675b018e7991d7e915a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ch=C5=82odnicki?= Date: Mon, 20 Mar 2023 14:25:55 -0700 Subject: [PATCH] feat(types): support nuxt types for `defineComponent` (#19789) --- packages/types/app/vue.d.ts | 278 ++++++++++++++++++++++++++++++++++-- packages/types/test/vue.ts | 169 +++++++++++++++++++++- 2 files changed, 436 insertions(+), 11 deletions(-) diff --git a/packages/types/app/vue.d.ts b/packages/types/app/vue.d.ts index 596909d05c..d1f3b90c11 100644 --- a/packages/types/app/vue.d.ts +++ b/packages/types/app/vue.d.ts @@ -6,7 +6,12 @@ import type Vue from 'vue' import type { MetaInfo } from 'vue-meta' import type { Route } from 'vue-router' import type { RecordPropsDefinition, ComponentOptions } from 'vue/types/options' -import type { ComponentOptionsMixin } from 'vue/types/v3-component-options' +import type { Data, HasDefined } from 'vue/types/common' +import type { ComponentOptionsMixin, ComputedOptions, ComponentOptionsBase, MethodOptions } from 'vue/types/v3-component-options' +import type { ComponentPropsOptions, ExtractDefaultPropTypes, ExtractPropTypes } from 'vue/types/v3-component-props' +import type { CreateComponentPublicInstance } from 'vue/types/v3-component-public-instance' +import type { DefineComponent } from 'vue/types/v3-define-component' +import type { EmitsOptions } from 'vue/types/v3-setup-context' import type { CombinedVueInstance } from 'vue/types/vue' import type { NuxtRuntimeConfig } from '../config/runtime' import type { Context, Middleware, Transition, NuxtApp } from './index' @@ -16,12 +21,150 @@ type DefaultData = object | ((this: V) => object) type DefaultProps = Record type DefaultMethods = { [key: string]: (this: V, ...args: any[]) => any } type DefaultComputed = { [key: string]: any } -type DefaultAsyncData = ((this: V, context: Context) => Promise | object | void) +type DefaultAsyncData = ((this: never, context: Context) => Promise | object | void) + +declare module 'vue/types' { + /** + * overload 1: object format with no props + */ + export function defineComponent< + RawBindings, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Emits extends EmitsOptions = {}, + EmitsNames extends string = string, + AsyncData extends DefaultAsyncData = DefaultAsyncData, + >( + options: { functional?: never } & ComponentOptionsWithoutPropsAndWithAsyncData< + {}, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + AsyncData + > + ): DefineComponent< + {}, + RawBindings, + Merged>>, + C, + M, + Mixin, + Extends, + Emits + > + + /** + * overload 2: object format with array props declaration + * props inferred as `{ [key in PropNames]?: any }` + * + * return type is for Vetur and TSX support + */ + export function defineComponent< + PropNames extends string, + RawBindings = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Emits extends EmitsOptions = {}, + EmitsNames extends string = string, + PropsOptions extends ComponentPropsOptions = ComponentPropsOptions, + AsyncData extends DefaultAsyncData = DefaultAsyncData, + >( + options: { functional?: never } & ComponentOptionsWithArrayPropsAndAsyncData< + PropNames, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + Readonly<{ [key in PropNames]?: any }>, + AsyncData + > + ): DefineComponent< + Readonly<{ [key in PropNames]?: any }>, + RawBindings, + Merged>>, + C, + M, + Mixin, + Extends, + Emits + > + + /** + * overload 3: object format with object props declaration + * + * see `ExtractPropTypes` in './componentProps.ts' + */ + export function defineComponent< + Props, + RawBindings = {}, + D = {}, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Emits extends EmitsOptions = {}, + EmitsNames extends string = string, + PropsOptions extends ComponentPropsOptions = ComponentPropsOptions, + AsyncData extends DefaultAsyncData = DefaultAsyncData, + >( + options: HasDefined extends true + ? { functional?: never } & ComponentOptionsWithPropsAndAsyncData< + PropsOptions, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + Props, + ExtractDefaultPropTypes, + AsyncData + > + : { functional?: never } & ComponentOptionsWithPropsAndAsyncData< + PropsOptions, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + ExtractPropTypes, + AsyncData + > + ): DefineComponent< + PropsOptions, + RawBindings, + Merged>>, + C, + M, + Mixin, + Extends, + Emits + > +} declare module 'vue/types/options' { interface ComponentOptions< V extends Vue, - /* eslint-disable no-unused-vars,@typescript-eslint/no-unused-vars */ Data = DefaultData, Methods = DefaultMethods, Computed = DefaultComputed, @@ -30,10 +173,8 @@ declare module 'vue/types/options' { RawBindings = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - /* eslint-enable no-unused-vars,@typescript-eslint/no-unused-vars */ - AsyncData = DefaultAsyncData + AsyncData = DefaultAsyncData > { - // eslint-disable-next-line @typescript-eslint/ban-types asyncData?: AsyncData fetch?(ctx: Context): Promise | void fetchKey?: string | ((getKey: (id: string) => number) => string) @@ -67,7 +208,7 @@ type ThisTypedComponentOptionsWithArrayPropsAndAsyncData< SetupBindings, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, - AsyncData extends DefaultAsyncData = DefaultAsyncData + AsyncData extends DefaultAsyncData = DefaultAsyncData > = object & ComponentOptions< V, @@ -102,7 +243,7 @@ export type ThisTypedComponentOptionsWithRecordPropsAndAsyncData< SetupBindings, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, - AsyncData extends DefaultAsyncData + AsyncData extends DefaultAsyncData > = object & ComponentOptions< V, @@ -151,7 +292,7 @@ declare module 'vue/types/vue' { SetupBindings = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - AsyncData extends DefaultAsyncData = DefaultAsyncData + AsyncData extends DefaultAsyncData = DefaultAsyncData >( options?: ThisTypedComponentOptionsWithArrayPropsAndAsyncData< V, @@ -184,7 +325,7 @@ declare module 'vue/types/vue' { SetupBindings = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, - AsyncData extends DefaultAsyncData = DefaultAsyncData + AsyncData extends DefaultAsyncData = DefaultAsyncData >( options?: ThisTypedComponentOptionsWithRecordPropsAndAsyncData< V, @@ -209,3 +350,120 @@ declare module 'vue/types/vue' { > } } + +export type ComponentOptionsWithPropsAndAsyncData< + PropsOptions = ComponentPropsOptions, + RawBindings = Data, + D = Data, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Emits extends EmitsOptions = {}, + EmitsNames extends string = string, + Props = ExtractPropTypes, + Defaults = ExtractDefaultPropTypes, + AsyncData extends DefaultAsyncData = DefaultAsyncData, +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + Defaults +> & { + props?: PropsOptions, + asyncData: AsyncData, +} & ThisType< + CreateComponentPublicInstance< + Props, + RawBindings, + Merged>>, + C, + M, + Mixin, + Extends, + Emits + > + > + +export type ComponentOptionsWithArrayPropsAndAsyncData< + PropNames extends string = string, + RawBindings = Data, + D = Data, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Emits extends EmitsOptions = {}, + EmitsNames extends string = string, + Props = Readonly<{ [key in PropNames]?: any }>, + AsyncData extends DefaultAsyncData = DefaultAsyncData, +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + {} +> & { + props?: PropNames[], + asyncData: AsyncData, +} & ThisType< + CreateComponentPublicInstance< + Props, + RawBindings, + Merged>>, + C, + M, + Mixin, + Extends, + Emits + > + > + +export type ComponentOptionsWithoutPropsAndWithAsyncData< + Props = {}, + RawBindings = Data, + D = Data, + C extends ComputedOptions = {}, + M extends MethodOptions = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + Emits extends EmitsOptions = {}, + EmitsNames extends string = string, + AsyncData extends DefaultAsyncData = DefaultAsyncData, +> = ComponentOptionsBase< + Props, + RawBindings, + D, + C, + M, + Mixin, + Extends, + Emits, + EmitsNames, + {} +> & { + props?: undefined, + asyncData: AsyncData, +} & ThisType< + CreateComponentPublicInstance< + Props, + RawBindings, + Merged>>, + C, + M, + Mixin, + Extends, + Emits + > + > diff --git a/packages/types/test/vue.ts b/packages/types/test/vue.ts index 409d83cef6..1d139477d2 100644 --- a/packages/types/test/vue.ts +++ b/packages/types/test/vue.ts @@ -2,8 +2,9 @@ * Test extended type definitions of Vue interfaces * @nuxt/types/app/vue.d.ts */ +/* eslint-disable vue/one-component-per-file */ -import Vue from 'vue' +import Vue, { defineComponent } from 'vue' import type { Middleware } from '..' const options: Vue.ComponentOptions = {} @@ -110,3 +111,169 @@ vm.$nuxt.$loading.start() vm.$nuxt.isOffline = true vm.$nuxt.isOnline = true + +// component options - defineComponent + +defineComponent({ + name: 'WithNoProps', + asyncData (context) { + return { + asyncDataProperty: context.base + } + }, + data () { + return { + normalDataProperty: 123 + } + }, + computed: { + dataPropertyGetter (): number { + return this.normalDataProperty + }, + asyncPropertyGetter (): string { + return this.asyncDataProperty + } + } +}) + +defineComponent({ + name: 'WithObjectProps', + props: { + boolProperty: { + type: Boolean, + required: true + } + }, + asyncData (context) { + return { + asyncDataProperty: context.base + } + }, + data () { + return { + normalDataProperty: 123 + } + }, + computed: { + boolPropertyGetter (): boolean { + return this.boolProperty + }, + dataPropertyGetter (): number { + return this.normalDataProperty + }, + asyncPropertyGetter (): string { + return this.asyncDataProperty + } + } +}) + +defineComponent({ + name: 'WithArrayProps', + // eslint-disable-next-line vue/require-prop-types + props: ['boolProperty'], + asyncData (context) { + return { + asyncDataProperty: context.base + } + }, + data () { + return { + normalDataProperty: 123 + } + }, + computed: { + boolPropertyGetter (): boolean { + // Typed as "any" + return this.boolProperty + }, + dataPropertyGetter (): number { + return this.normalDataProperty + }, + asyncPropertyGetter (): string { + return this.asyncDataProperty + } + } +}) + +// component options - Vue.extend + +Vue.extend({ + name: 'WithNoProps', + asyncData (context) { + return { + asyncDataProperty: context.base + } + }, + data () { + return { + normalDataProperty: 123 + } + }, + computed: { + dataPropertyGetter (): number { + return this.normalDataProperty + }, + asyncPropertyGetter (): string { + return this.asyncDataProperty + } + } +}) + +Vue.extend({ + name: 'WithObjectProps', + props: { + boolProperty: { + type: Boolean, + required: true + } + }, + asyncData (context) { + return { + asyncDataProperty: context.base + } + }, + data () { + return { + normalDataProperty: 123 + } + }, + computed: { + boolPropertyGetter (): boolean { + return this.boolProperty + }, + dataPropertyGetter (): number { + return this.normalDataProperty + }, + asyncPropertyGetter (): string { + return this.asyncDataProperty + } + } +}) + +Vue.extend({ + name: 'WithArrayProps', + // eslint-disable-next-line vue/require-prop-types + props: ['boolProperty'], + asyncData (context) { + return { + asyncDataProperty: context.base + } + }, + data () { + return { + normalDataProperty: 123 + } + }, + computed: { + boolPropertyGetter (): boolean { + // Typed as "any" + return this.boolProperty + }, + dataPropertyGetter (): number { + return this.normalDataProperty + }, + asyncPropertyGetter (): string { + return this.asyncDataProperty + } + } +})