From e0a16564658ee40fcebd99a06c91ae36f300ae8a Mon Sep 17 00:00:00 2001 From: pikax Date: Mon, 30 Nov 2020 17:42:35 +0000 Subject: [PATCH 1/9] types(slots): Add typed slots --- .../runtime-core/src/apiDefineComponent.ts | 33 ++++++++++++------- packages/runtime-core/src/component.ts | 4 +-- packages/runtime-core/src/componentOptions.ts | 30 ++++++++++++++--- .../src/componentPublicInstance.ts | 19 +++++++++-- packages/runtime-core/src/componentSlots.ts | 23 +++++++++++-- test-dts/defineComponent.test-d.tsx | 25 ++++++++++++++ 6 files changed, 112 insertions(+), 22 deletions(-) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 333d18a30f3..b5eb963609d 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -25,6 +25,7 @@ import { CreateComponentPublicInstance, ComponentPublicInstanceConstructor } from './componentPublicInstance' +import { Slots } from './componentSlots' export type PublicProps = VNodeProps & AllowedComponentProps & @@ -40,6 +41,7 @@ export type DefineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, EE extends string = string, + S = {}, PP = PublicProps, Props = Readonly>, Defaults = ExtractDefaultPropTypes @@ -53,6 +55,7 @@ export type DefineComponent< Mixin, Extends, E, + S, PP & Props, Defaults, true @@ -69,6 +72,7 @@ export type DefineComponent< Extends, E, EE, + S, Defaults > & PP @@ -80,12 +84,12 @@ export type DefineComponent< // overload 1: direct setup function // (uses user defined props interface) -export function defineComponent( +export function defineComponent( setup: ( props: Readonly, - ctx: SetupContext + ctx: SetupContext> ) => RawBindings | RenderFunction -): DefineComponent +): DefineComponent // overload 2: object format with no props // (uses user defined props interface) @@ -99,7 +103,8 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, - EE extends string = string + EE extends string = string, + S = {} >( options: ComponentOptionsWithoutProps< Props, @@ -110,9 +115,10 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + S > -): DefineComponent +): DefineComponent // overload 3: object format with array props declaration // props inferred as { [key in PropNames]?: any } @@ -126,7 +132,8 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, - EE extends string = string + EE extends string = string, + S = {} >( options: ComponentOptionsWithArrayProps< PropNames, @@ -137,7 +144,8 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + S > ): DefineComponent< Readonly<{ [key in PropNames]?: any }>, @@ -148,7 +156,8 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + S > // overload 4: object format with object props declaration @@ -164,7 +173,8 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, - EE extends string = string + EE extends string = string, + S = {} >( options: ComponentOptionsWithObjectProps< PropsOptions, @@ -175,7 +185,8 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + S > ): DefineComponent diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 9b1fdcef18f..31ab9e73d1f 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -167,9 +167,9 @@ export const enum LifecycleHooks { ERROR_CAPTURED = 'ec' } -export interface SetupContext { +export interface SetupContext { attrs: Data - slots: Slots + slots: S emit: EmitFn expose: (exposed: Record) => void } diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 89bd8c06053..b776840223c 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -59,6 +59,7 @@ import { import { warn } from './warning' import { VNodeChild } from './vnode' import { callWithAsyncErrorHandling } from './errorHandling' +import { Slots } from './componentSlots' /** * Interface for declaring custom options. @@ -90,6 +91,7 @@ export interface ComponentOptionsBase< Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, + S = {}, Defaults = {} > extends LegacyOptions, @@ -98,7 +100,7 @@ export interface ComponentOptionsBase< setup?: ( this: void, props: Props, - ctx: SetupContext + ctx: SetupContext> ) => Promise | RawBindings | RenderFunction | void name?: string template?: string | object // can be a direct DOM node @@ -116,6 +118,8 @@ export interface ComponentOptionsBase< expose?: string[] serverPrefetch?(): Promise + slots?: S & ThisType + // Internal ------------------------------------------------------------------ /** @@ -178,7 +182,8 @@ export type ComponentOptionsWithoutProps< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, - EE extends string = string + EE extends string = string, + S = {} > = ComponentOptionsBase< Props, RawBindings, @@ -189,11 +194,22 @@ export type ComponentOptionsWithoutProps< Extends, E, EE, + S, {} > & { props?: undefined } & ThisType< - CreateComponentPublicInstance<{}, RawBindings, D, C, M, Mixin, Extends, E> + CreateComponentPublicInstance< + {}, + RawBindings, + D, + C, + M, + Mixin, + Extends, + E, + S + > > export type ComponentOptionsWithArrayProps< @@ -206,6 +222,7 @@ export type ComponentOptionsWithArrayProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + S = {}, Props = Readonly<{ [key in PropNames]?: any }> > = ComponentOptionsBase< Props, @@ -217,6 +234,7 @@ export type ComponentOptionsWithArrayProps< Extends, E, EE, + S, {} > & { props: PropNames[] @@ -229,7 +247,8 @@ export type ComponentOptionsWithArrayProps< M, Mixin, Extends, - E + E, + S > > @@ -243,6 +262,7 @@ export type ComponentOptionsWithObjectProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + S = {}, Props = Readonly>, Defaults = ExtractDefaultPropTypes > = ComponentOptionsBase< @@ -255,6 +275,7 @@ export type ComponentOptionsWithObjectProps< Extends, E, EE, + S, Defaults > & { props: PropsOptions & ThisType @@ -268,6 +289,7 @@ export type ComponentOptionsWithObjectProps< Mixin, Extends, E, + S, Props, Defaults, false diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 1e62f8b16c5..3d980085d5c 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -79,6 +79,7 @@ type MixinToOptionTypes = T extends ComponentOptionsBase< infer Extends, any, any, + any, infer Defaults > ? OptionTypesType

& @@ -131,6 +132,7 @@ export type CreateComponentPublicInstance< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, + S = {}, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, @@ -151,10 +153,11 @@ export type CreateComponentPublicInstance< PublicC, PublicM, E, + S, PublicProps, PublicDefaults, MakeDefaultsOptional, - ComponentOptionsBase + ComponentOptionsBase > // public properties exposed on the proxy, which is used as the render context @@ -166,10 +169,22 @@ export type ComponentPublicInstance< C extends ComputedOptions = {}, M extends MethodOptions = {}, E extends EmitsOptions = {}, + S = {}, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, - Options = ComponentOptionsBase + Options = ComponentOptionsBase< + any, + any, + any, + any, + any, + any, + any, + any, + any, + any + > > = { $: ComponentInternalInstance $data: D diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 4a6a1b75375..ea7d62f055c 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -22,11 +22,28 @@ import { isHmrUpdating } from './hmr' export type Slot = (...args: any[]) => VNode[] -export type InternalSlots = { - [name: string]: Slot | undefined +export type SlotTyped = T extends null ? () => VNode[] : (arg: T) => VNode[] + +export type InternalSlots = { + [K in keyof T]?: T[K] extends () => infer R ? SlotTyped : SlotTyped } -export type Slots = Readonly +export type SlotsObject = InternalSlots +export type SlotArray = V extends PropertyKey + ? Record + : Record + +export type Slots = unknown extends T + ? Readonly>> + : T extends Array ? Readonly> : Readonly> + +// export type Slot = (...args: any[]) => VNode[] + +// export type InternalSlots = { +// [name: string]: Slot | undefined +// } + +// export type Slots = Readonly export type RawSlots = { [name: string]: unknown diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index c77feba025d..4da454eae60 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -924,6 +924,31 @@ describe('async setup', () => { vm.a = 2 }) +describe('typed slots', () => { + const xxx = defineComponent({ + slots: { + test: null as Partial, + item: Object as () => { item: { value: number }; i: number } + }, + + setup(_, { slots }) { + slots.test!() + slots.item!({ + i: 22, + item: { + value: 22 + } + }) + + // @ts-expect-error missing item prop + expectError(slots.item!({ i: 22 })) + } + }) + + // TODO add to `h` + h(xxx, {}, {}) +}) + // check if defineComponent can be exported export default { // function components From b4dc05f7295321f1c0a404f3ff2f80d5f7d62678 Mon Sep 17 00:00:00 2001 From: pikax Date: Mon, 30 Nov 2020 17:48:48 +0000 Subject: [PATCH 2/9] chore: fix tests --- packages/runtime-core/src/apiDefineComponent.ts | 8 ++++---- packages/runtime-core/src/componentOptions.ts | 8 ++++---- packages/runtime-core/src/componentPublicInstance.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index b5eb963609d..f7f6d625019 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -41,7 +41,7 @@ export type DefineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, EE extends string = string, - S = {}, + S = any, PP = PublicProps, Props = Readonly>, Defaults = ExtractDefaultPropTypes @@ -104,7 +104,7 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = {} + S = any >( options: ComponentOptionsWithoutProps< Props, @@ -133,7 +133,7 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, EE extends string = string, - S = {} + S = any >( options: ComponentOptionsWithArrayProps< PropNames, @@ -174,7 +174,7 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, EE extends string = string, - S = {} + S = any >( options: ComponentOptionsWithObjectProps< PropsOptions, diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index b776840223c..477dfb485c7 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -91,7 +91,7 @@ export interface ComponentOptionsBase< Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, - S = {}, + S = any, Defaults = {} > extends LegacyOptions, @@ -183,7 +183,7 @@ export type ComponentOptionsWithoutProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = {} + S = any > = ComponentOptionsBase< Props, RawBindings, @@ -222,7 +222,7 @@ export type ComponentOptionsWithArrayProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = {}, + S = any, Props = Readonly<{ [key in PropNames]?: any }> > = ComponentOptionsBase< Props, @@ -262,7 +262,7 @@ export type ComponentOptionsWithObjectProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = {}, + S = any, Props = Readonly>, Defaults = ExtractDefaultPropTypes > = ComponentOptionsBase< diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 3d980085d5c..d4ec425813b 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -132,7 +132,7 @@ export type CreateComponentPublicInstance< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, - S = {}, + S = any, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, @@ -169,7 +169,7 @@ export type ComponentPublicInstance< C extends ComputedOptions = {}, M extends MethodOptions = {}, E extends EmitsOptions = {}, - S = {}, + S = any, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, From d729fba0b9ed33660ec64997b3858b663a910766 Mon Sep 17 00:00:00 2001 From: pikax Date: Sat, 13 Mar 2021 15:58:57 +0000 Subject: [PATCH 3/9] chore: added support for `h` --- packages/runtime-core/src/component.ts | 9 ++-- .../src/componentPublicInstance.ts | 2 +- packages/runtime-core/src/componentSlots.ts | 25 ++++----- packages/runtime-core/src/h.ts | 10 ++-- test-dts/defineComponent.test-d.tsx | 51 +++++++++++++++++-- 5 files changed, 70 insertions(+), 27 deletions(-) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index f16ffeef100..41189f4d61d 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -99,10 +99,13 @@ export interface ComponentInternalOptions { __file?: string } -export interface FunctionalComponent

- extends ComponentInternalOptions { +export interface FunctionalComponent< + P = {}, + E extends EmitsOptions = {}, + S extends Slots = Slots +> extends ComponentInternalOptions { // use of any here is intentional so it can be a valid JSX Element constructor - (props: P, ctx: Omit, 'expose'>): any + (props: P, ctx: Omit, 'expose'>): any props?: ComponentPropsOptions

emits?: E | (keyof E)[] inheritAttrs?: boolean diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 00bbbaf2f00..30c8419ce6c 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -195,7 +195,7 @@ export type ComponentPublicInstance< : P & PublicProps $attrs: Data $refs: Data - $slots: Slots + $slots: Slots $root: ComponentPublicInstance | null $parent: ComponentPublicInstance | null $emit: EmitFn diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index b3cbd2b49ed..1a2fe2badba 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -33,20 +33,14 @@ export type SlotArray = V extends PropertyKey ? Record : Record -export type Slots = unknown extends T - ? Readonly>> - : T extends Array ? Readonly> : Readonly> - -// export type Slot = (...args: any[]) => VNode[] - -// export type InternalSlots = { -// [name: string]: Slot | undefined -// } - -// export type Slots = Readonly - -export type RawSlots = { - [name: string]: unknown +export type Slots = RenderSlot & + (unknown extends T + ? Readonly>> + : T extends Array + ? Readonly> + : Readonly>) + +export type RenderSlot = { // manual render fn hint to skip forced children updates $stable?: boolean /** @@ -65,6 +59,9 @@ export type RawSlots = { */ _?: SlotFlags } +export type RawSlots = { + [name: string]: unknown +} & RenderSlot const isInternalKey = (key: string) => key[0] === '_' || key === '$stable' diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index cb87cf6ee3c..1f67ed4fd0d 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -66,11 +66,11 @@ type RawChildren = | (() => any) // fake constructor type returned from `defineComponent` -interface Constructor

{ +interface Constructor

{ __isFragment?: never __isTeleport?: never __isSuspense?: never - new (...args: any[]): { $props: P } + new (...args: any[]): { $props: P; $slots?: S } } // The following is a series of overloads for providing props validation of @@ -144,10 +144,10 @@ export function h

( // fake constructor type returned by `defineComponent` or class component export function h(type: Constructor, children?: RawChildren): VNode -export function h

( - type: Constructor

, +export function h( + type: Constructor, props?: (RawProps & P) | ({} extends P ? null : never), - children?: RawChildren | RawSlots + children?: (RawChildren & S) | ({} extends S ? RawSlots : S) ): VNode // fake constructor type returned by `defineComponent` diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 0ec0cf0d045..4f8a979108c 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -964,9 +964,9 @@ describe('async setup', () => { }) describe('typed slots', () => { - const xxx = defineComponent({ + const Comp = defineComponent({ slots: { - test: null as Partial, + test: null, item: Object as () => { item: { value: number }; i: number } }, @@ -978,14 +978,57 @@ describe('typed slots', () => { value: 22 } }) + // @ts-expect-error missing item prop + expectError(slots.item!({ i: 22 })) + } + }) + + h( + Comp, + {}, + { + // @ts-expect-error no argument expected + test(x) {}, + item(s) { + expectType(s.i) + expectType<{ value: number }>(s.item) + } + } + ) +}) + +describe('typed slots just type', () => { + const Comp = defineComponent({ + slots: {} as { + test: null + item: { item: { value: number }; i: number } + }, + setup(_, { slots }) { + slots.test!() + slots.item!({ + i: 22, + item: { + value: 22 + } + }) // @ts-expect-error missing item prop expectError(slots.item!({ i: 22 })) } }) - // TODO add to `h` - h(xxx, {}, {}) + h( + Comp, + {}, + { + // @ts-expect-error no argument expected + test(x) {}, + item(s) { + expectType(s.i) + expectType<{ value: number }>(s.item) + } + } + ) }) // check if defineComponent can be exported From 53adc923fff34457d0bba8ff9830dc2f2aa64373 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Mon, 20 Feb 2023 07:09:09 +0000 Subject: [PATCH 4/9] rollback props test implementation defineComponent test --- packages/dts-test/defineComponent.test-d.tsx | 188 +++++++++---------- 1 file changed, 92 insertions(+), 96 deletions(-) diff --git a/packages/dts-test/defineComponent.test-d.tsx b/packages/dts-test/defineComponent.test-d.tsx index c2bcc07eca6..57c37826a4f 100644 --- a/packages/dts-test/defineComponent.test-d.tsx +++ b/packages/dts-test/defineComponent.test-d.tsx @@ -10,11 +10,7 @@ import { SetupContext, h } from 'vue' -import { - describe, - expectType, - IsUnion, -} from './utils' +import { describe, expectType, IsUnion } from './utils' describe('with object props', () => { interface ExpectedProps { @@ -50,96 +46,98 @@ describe('with object props', () => { type GT = string & { __brand: unknown } - const MyComponent = defineComponent({ - props: { - a: Number, - // required should make property non-void - b: { - type: String, - required: true - }, - e: Function, - h: Boolean, - j: Function as PropType string | undefined)>, - // default value should infer type and make it non-void - bb: { - default: 'hello' - }, - bbb: { - // Note: default function value requires arrow syntax + explicit - // annotation - default: (props: any) => (props.bb as string) || 'foo' - }, - bbbb: { - type: String, - default: undefined - }, - bbbbb: { - type: String, - default: () => undefined - }, - // explicit type casting - cc: Array as PropType, - // required + type casting - dd: { - type: Object as PropType<{ n: 1 }>, - required: true - }, - // return type - ee: Function as PropType<() => string>, - // arguments + object return - ff: Function as PropType<(a: number, b: string) => { a: boolean }>, - // explicit type casting with constructor - ccc: Array as () => string[], - // required + contructor type casting - ddd: { - type: Array as () => string[], - required: true - }, - // required + object return - eee: { - type: Function as PropType<() => { a: string }>, - required: true - }, - // required + arguments + object return - fff: { - type: Function as PropType<(a: number, b: string) => { a: boolean }>, - required: true - }, - hhh: { - type: Boolean, - required: true - }, - // default + type casting - ggg: { - type: String as PropType<'foo' | 'bar'>, - default: 'foo' - }, - // default + function - ffff: { - type: Function as PropType<(a: number, b: string) => { a: boolean }>, - default: (a: number, b: string) => ({ a: a > +b }) - }, - // union + function with different return types - iii: Function as PropType<(() => string) | (() => number)>, - // union + function with different args & same return type - jjj: { - type: Function as PropType< - ((arg1: string) => string) | ((arg1: string, arg2: string) => string) - >, - required: true - }, - kkk: null, - validated: { - type: String, - // validator requires explicit annotation - validator: (val: unknown) => val !== '' - }, - date: Date, - l: [Date], - ll: [Date, Number], - lll: [String, Number] + const props = { + a: Number, + // required should make property non-void + b: { + type: String, + required: true + }, + e: Function, + h: Boolean, + j: Function as PropType string | undefined)>, + // default value should infer type and make it non-void + bb: { + default: 'hello' + }, + bbb: { + // Note: default function value requires arrow syntax + explicit + // annotation + default: (props: any) => (props.bb as string) || 'foo' + }, + bbbb: { + type: String, + default: undefined + }, + bbbbb: { + type: String, + default: () => undefined + }, + // explicit type casting + cc: Array as PropType, + // required + type casting + dd: { + type: Object as PropType<{ n: 1 }>, + required: true + }, + // return type + ee: Function as PropType<() => string>, + // arguments + object return + ff: Function as PropType<(a: number, b: string) => { a: boolean }>, + // explicit type casting with constructor + ccc: Array as () => string[], + // required + contructor type casting + ddd: { + type: Array as () => string[], + required: true }, + // required + object return + eee: { + type: Function as PropType<() => { a: string }>, + required: true + }, + // required + arguments + object return + fff: { + type: Function as PropType<(a: number, b: string) => { a: boolean }>, + required: true + }, + hhh: { + type: Boolean, + required: true + }, + // default + type casting + ggg: { + type: String as PropType<'foo' | 'bar'>, + default: 'foo' + }, + // default + function + ffff: { + type: Function as PropType<(a: number, b: string) => { a: boolean }>, + default: (a: number, b: string) => ({ a: a > +b }) + }, + // union + function with different return types + iii: Function as PropType<(() => string) | (() => number)>, + // union + function with different args & same return type + jjj: { + type: Function as PropType< + ((arg1: string) => string) | ((arg1: string, arg2: string) => string) + >, + required: true + }, + kkk: null, + validated: { + type: String, + // validator requires explicit annotation + validator: (val: unknown) => val !== '' + }, + date: Date, + l: [Date], + ll: [Date, Number], + lll: [String, Number] + } + + const MyComponent = defineComponent({ + props, setup(props) { // type assertion. See https://github.com/SamVerschueren/tsd expectType(props.a) @@ -1252,7 +1250,6 @@ describe('prop starting with `on*` is broken', () => { }) }) - describe('typed slots', () => { const Comp = defineComponent({ slots: { @@ -1368,4 +1365,3 @@ declare const MyButton: DefineComponent< {} > ; - From cc7e1c16a729e0879301c91aeb3d3014651958b4 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Mon, 20 Feb 2023 07:36:50 +0000 Subject: [PATCH 5/9] fix some errors --- packages/dts-test/defineComponent.test-d.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/dts-test/defineComponent.test-d.tsx b/packages/dts-test/defineComponent.test-d.tsx index 57c37826a4f..78c1dd495ee 100644 --- a/packages/dts-test/defineComponent.test-d.tsx +++ b/packages/dts-test/defineComponent.test-d.tsx @@ -51,7 +51,7 @@ describe('with object props', () => { // required should make property non-void b: { type: String, - required: true + required: true as true }, e: Function, h: Boolean, @@ -78,7 +78,7 @@ describe('with object props', () => { // required + type casting dd: { type: Object as PropType<{ n: 1 }>, - required: true + required: true as true }, // return type ee: Function as PropType<() => string>, @@ -89,21 +89,21 @@ describe('with object props', () => { // required + contructor type casting ddd: { type: Array as () => string[], - required: true + required: true as true }, // required + object return eee: { type: Function as PropType<() => { a: string }>, - required: true + required: true as true }, // required + arguments + object return fff: { type: Function as PropType<(a: number, b: string) => { a: boolean }>, - required: true + required: true as true }, hhh: { type: Boolean, - required: true + required: true as true }, // default + type casting ggg: { @@ -122,7 +122,7 @@ describe('with object props', () => { type: Function as PropType< ((arg1: string) => string) | ((arg1: string, arg2: string) => string) >, - required: true + required: true as true }, kkk: null, validated: { From 479aaf1ff7b7eae04d92f2e13dd965a943da2356 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Tue, 21 Feb 2023 17:23:54 +0000 Subject: [PATCH 6/9] improve --- packages/dts-test/defineComponent.test-d.tsx | 1 + packages/runtime-core/src/apiDefineComponent.ts | 17 ++++++++++++++--- .../runtime-core/src/componentPublicInstance.ts | 17 ++++++++++++++--- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/dts-test/defineComponent.test-d.tsx b/packages/dts-test/defineComponent.test-d.tsx index 78c1dd495ee..5a600d13365 100644 --- a/packages/dts-test/defineComponent.test-d.tsx +++ b/packages/dts-test/defineComponent.test-d.tsx @@ -1360,6 +1360,7 @@ declare const MyButton: DefineComponent< ComponentOptionsMixin, EmitsOptions, string, + {}, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly>, {} diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 8ef90c0a48b..3b23ae59a78 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -42,7 +42,7 @@ export type DefineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, - S = any, + S = {}, PP = PublicProps, Props = Readonly< PropsOrPropOptions extends ComponentPropsOptions @@ -95,7 +95,18 @@ export function defineComponent( props: Readonly, ctx: SetupContext> ) => RawBindings | RenderFunction -): DefineComponent +): DefineComponent< + Props, + RawBindings, + {}, + ComputedOptions, + MethodOptions, + ComponentOptionsMixin, + ComponentOptionsMixin, + {}, + string, + S +> // overload 2: object format with no props // (uses user defined props interface) @@ -157,7 +168,7 @@ export function defineComponent< Extends, E, EE, - S, + S, I, II > diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index c86f5988df5..a6040850b0f 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -141,7 +141,7 @@ export type CreateComponentPublicInstance< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, - S = any, + S = {}, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, @@ -180,11 +180,22 @@ export type ComponentPublicInstance< C extends ComputedOptions = {}, M extends MethodOptions = {}, E extends EmitsOptions = {}, - S = any, + S = {}, PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, - Options = ComponentOptionsBase, + Options = ComponentOptionsBase< + any, + any, + any, + any, + any, + any, + any, + any, + any, + any + >, I extends ComponentInjectOptions = {} > = { $: ComponentInternalInstance From 3785f75dc7b78e8627e11335c4affa1a5c7840b0 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Tue, 21 Feb 2023 17:51:47 +0000 Subject: [PATCH 7/9] minor improvements to the type --- packages/dts-test/defineComponent.test-d.tsx | 146 +++++++++++------- .../runtime-core/src/apiDefineComponent.ts | 6 +- packages/runtime-core/src/componentOptions.ts | 8 +- packages/runtime-core/src/componentSlots.ts | 4 +- packages/runtime-core/src/h.ts | 13 +- 5 files changed, 111 insertions(+), 66 deletions(-) diff --git a/packages/dts-test/defineComponent.test-d.tsx b/packages/dts-test/defineComponent.test-d.tsx index 5a600d13365..ff90d24f868 100644 --- a/packages/dts-test/defineComponent.test-d.tsx +++ b/packages/dts-test/defineComponent.test-d.tsx @@ -86,7 +86,7 @@ describe('with object props', () => { ff: Function as PropType<(a: number, b: string) => { a: boolean }>, // explicit type casting with constructor ccc: Array as () => string[], - // required + contructor type casting + // required + constructor type casting ddd: { type: Array as () => string[], required: true as true @@ -1251,71 +1251,105 @@ describe('prop starting with `on*` is broken', () => { }) describe('typed slots', () => { - const Comp = defineComponent({ - slots: { - test: null, - item: Object as () => { item: { value: number }; i: number } - }, + describe('Object declaration', () => { + const Comp = defineComponent({ + slots: { + test: null, + item: Object as () => { item: { value: number }; i: number } + }, + + setup(_, { slots }) { + slots.test!() + slots.item!({ + i: 22, + item: { + value: 22 + } + }) + // @ts-expect-error missing item prop + expectError(slots.item!({ i: 22 })) + } + }) - setup(_, { slots }) { - slots.test!() - slots.item!({ - i: 22, - item: { - value: 22 + h( + Comp, + {}, + { + // @ts-expect-error no argument expected + test(x) {}, + item(s) { + expectType(s.i) + expectType<{ value: number }>(s.item) } - }) - // @ts-expect-error missing item prop - expectError(slots.item!({ i: 22 })) - } + } + ) + + h(Comp, {}, {}) }) - h( - Comp, - {}, - { - // @ts-expect-error no argument expected - test(x) {}, - item(s) { - expectType(s.i) - expectType<{ value: number }>(s.item) - } - } - ) -}) + describe('Type API', () => { + const Comp = defineComponent({ + slots: {} as { + test: null + item: { item: { value: number }; i: number } + }, -describe('typed slots just type', () => { - const Comp = defineComponent({ - slots: {} as { - test: null - item: { item: { value: number }; i: number } - }, + setup(_, { slots }) { + slots.test!() + slots.item!({ + i: 22, + item: { + value: 22 + } + }) + // @ts-expect-error missing item prop + slots.item!({ i: 22 }) + } + }) - setup(_, { slots }) { - slots.test!() - slots.item!({ - i: 22, - item: { - value: 22 + h( + Comp, + {}, + { + item() {}, + test() {} + } + ) + + h( + Comp, + {}, + { + // @ts-expect-error no argument expected + test(x) {}, + item(s) { + expectType(s.i) + expectType<{ value: number }>(s.item) } - }) - // @ts-expect-error missing item prop - expectError(slots.item!({ i: 22 })) - } + } + ) + h(Comp, {}, {}) }) - h( - Comp, - {}, - { - // @ts-expect-error no argument expected - test(x) {}, - item(s) { - expectType(s.i) - expectType<{ value: number }>(s.item) + describe('string Array', () => { + const Comp = defineComponent({ + slots: ['test', 'item'] as const, + + setup(_, { slots }) { + slots.test!() + slots.item!({ + i: 22, + item: { + value: 22 + } + }) + // @ts-expect-error not a valid slot + slots.other!({ i: 22 }) } - } - ) + }) + + h(Comp, {}, {}) + }) }) // check if defineComponent can be exported diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index 3b23ae59a78..c5965e86253 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -121,7 +121,7 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, - S = any, + S = {}, I extends ComponentInjectOptions = {}, II extends string = string >( @@ -154,7 +154,7 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, - S = any, + S = {}, I extends ComponentInjectOptions = {}, II extends string = string >( @@ -199,7 +199,7 @@ export function defineComponent< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, - S = any, + S = {}, I extends ComponentInjectOptions = {}, II extends string = string >( diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index daf9c38baa4..8b2bcea0fa6 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -119,7 +119,7 @@ export interface ComponentOptionsBase< Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, - S = any, + S = {}, Defaults = {}, I extends ComponentInjectOptions = {}, II extends string = string @@ -231,7 +231,7 @@ export type ComponentOptionsWithoutProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = any, + S = {}, I extends ComponentInjectOptions = {}, II extends string = string, PE = Props & EmitsToProps @@ -279,7 +279,7 @@ export type ComponentOptionsWithArrayProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = any, + S = {}, I extends ComponentInjectOptions = {}, II extends string = string, Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps @@ -327,7 +327,7 @@ export type ComponentOptionsWithObjectProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - S = any, + S = {}, I extends ComponentInjectOptions = {}, II extends string = string, Props = Readonly> & EmitsToProps, diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index be73c2aa2f2..1430a412386 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -31,7 +31,7 @@ export type InternalSlots = { } export type SlotsObject = InternalSlots -export type SlotArray = V extends PropertyKey +export type SlotArray = [V] extends [PropertyKey] ? Record : Record @@ -40,6 +40,8 @@ export type Slots = RenderSlot & ? Readonly>> : T extends Array ? Readonly> + : T extends ReadonlyArray + ? Readonly> : Readonly>) export type RenderSlot = { diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index bbd969d0678..7a97084f530 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -72,7 +72,10 @@ interface Constructor

{ __isFragment?: never __isTeleport?: never __isSuspense?: never - new (...args: any[]): { $props: P; $slots?: S } + new (...args: any[]): { + $props: P + $slots?: S + } } // The following is a series of overloads for providing props validation of @@ -159,7 +162,13 @@ export function h(type: Constructor, children?: RawChildren): VNode export function h( type: Constructor, props?: (RawProps & P) | ({} extends P ? null : never), - children?: (RawChildren & S) | ({} extends S ? RawSlots : S) + children?: (RawChildren & S) | ({} extends S ? RawSlots : Partial) +): VNode +// for the string array slot declaration +export function h( + type: Constructor, + props?: (RawProps & P) | ({} extends P ? null : never), + children?: RawChildren & Partial ): VNode // fake constructor type returned by `defineComponent` From 1fdac744ca65d9574edb6ffda80fae227b9e029f Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Tue, 21 Feb 2023 18:29:50 +0000 Subject: [PATCH 8/9] feat(compiler): added macros defineSlots --- .../__snapshots__/compileScript.spec.ts.snap | 65 ++++++++++ .../__tests__/compileScript.spec.ts | 64 +++++++++- packages/compiler-sfc/src/compileScript.ts | 111 +++++++++++++++++- .../__tests__/apiSetupHelpers.spec.ts | 4 + .../__tests__/components/KeepAlive.spec.ts | 5 + .../runtime-core/src/apiDefineComponent.ts | 14 +++ packages/runtime-core/src/apiSetupHelpers.ts | 34 ++++++ packages/runtime-core/src/index.ts | 1 + .../types/scriptSetupHelpers.d.ts | 2 + 9 files changed, 295 insertions(+), 5 deletions(-) diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index cbe511f1d07..9c9860ba36f 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -739,6 +739,71 @@ return { a, props, emit } }" `; +exports[`SFC compile + `) + assertCode(content) + expect(bindings).toStrictEqual({ + mySlots: BindingTypes.SETUP_CONST + }) + // should remove defineOptions import and call + expect(content).not.toMatch('defineSlots') + // should generate correct setup signature + expect(content).toMatch(`setup(__props, { expose, slots: mySlots }) {`) + // should include context options in default export + expect(content).toMatch(`export default { + slots: ['foo', 'bar'],`) + }) + + test('defineProps/defineSlots in multi-variable declaration', () => { + const { content } = compile(` + + `) + assertCode(content) + expect(content).toMatch(`const a = 1;`) // test correct removal + expect(content).toMatch(`props: ['item'],`) + expect(content).toMatch(`slots: ['a'],`) + }) + + test('defineProps/defineSlots in multi-variable declaration fix #6757 ', () => { + const { content } = compile(` + + `) + assertCode(content) + expect(content).toMatch(`const a = 1;`) // test correct removal + expect(content).toMatch(`props: ['item'],`) + expect(content).toMatch(`slots: ['a'],`) + }) + + test('defineProps/defineSlots in multi-variable declaration (full removal)', () => { + const { content } = compile(` + + `) + assertCode(content) + expect(content).toMatch(`props: ['item'],`) + expect(content).toMatch(`slots: ['a'],`) + }) + test('defineExpose()', () => { const { content } = compile(`