From 77d5e88608d5c66f8bd5d87058e440b7115ec2bf Mon Sep 17 00:00:00 2001 From: Andy Li Date: Thu, 15 Aug 2024 10:28:40 +0800 Subject: [PATCH 1/6] fix(types/custom-element): `defineCustomElement` with required props (#11578) --- .../dts-test/defineCustomElement.test-d.ts | 33 +++++++++++++++++++ packages/runtime-dom/src/apiCustomElement.ts | 8 ++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/dts-test/defineCustomElement.test-d.ts b/packages/dts-test/defineCustomElement.test-d.ts index b81c0befe9f..f81f8b8fa61 100644 --- a/packages/dts-test/defineCustomElement.test-d.ts +++ b/packages/dts-test/defineCustomElement.test-d.ts @@ -99,4 +99,37 @@ describe('defineCustomElement using defineComponent return type', () => { expectType(instance.a) instance.a = 42 }) + + test('with required props', () => { + const Comp1Vue = defineComponent({ + props: { + a: { type: Number, required: true }, + }, + }) + const Comp = defineCustomElement(Comp1Vue) + expectType(Comp) + + const instance = new Comp() + expectType(instance.a) + instance.a = 42 + }) + + test('with default props', () => { + const Comp1Vue = defineComponent({ + props: { + a: { + type: Number, + default: 1, + validator: () => true, + }, + }, + emits: ['click'], + }) + const Comp = defineCustomElement(Comp1Vue) + expectType(Comp) + + const instance = new Comp() + expectType(instance.a) + instance.a = 42 + }) }) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index efee4d8a9c1..2b472e73027 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -9,6 +9,7 @@ import { type ComponentOptionsBase, type ComponentOptionsMixin, type ComponentProvideOptions, + type ComponentPublicInstance, type ComputedOptions, type ConcreteComponent, type CreateAppFunction, @@ -153,14 +154,13 @@ export function defineCustomElement< // overload 3: defining a custom element from the returned value of // `defineComponent` export function defineCustomElement< - T extends DefineComponent, + // this should be `ComponentPublicInstanceConstructor` but that type is not exported + T extends { new (...args: any[]): ComponentPublicInstance }, >( options: T, extraOptions?: CustomElementOptions, ): VueElementConstructor< - T extends DefineComponent - ? ExtractPropTypes

- : unknown + T extends DefineComponent ? P : unknown > /*! #__NO_SIDE_EFFECTS__ */ From f60944650c2d785a0cb390e2404161b18d814f26 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 16 Aug 2024 09:06:58 +0800 Subject: [PATCH 2/6] fix(reactivity): extended methods respect reactive --- .../__tests__/reactiveArray.spec.ts | 13 +++++++++-- .../reactivity/src/arrayInstrumentations.ts | 23 +++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/reactivity/__tests__/reactiveArray.spec.ts b/packages/reactivity/__tests__/reactiveArray.spec.ts index 960c16e0b3b..c04bf070f73 100644 --- a/packages/reactivity/__tests__/reactiveArray.spec.ts +++ b/packages/reactivity/__tests__/reactiveArray.spec.ts @@ -2,6 +2,7 @@ import { type ComputedRef, computed } from '../src/computed' import { isReactive, reactive, shallowReactive, toRaw } from '../src/reactive' import { isRef, ref } from '../src/ref' import { effect } from '../src/effect' +import { expect } from 'vitest' describe('reactivity/reactive/Array', () => { test('should make Array reactive', () => { @@ -709,9 +710,17 @@ describe('reactivity/reactive/Array', () => { expect(state.things.every('foo', 'bar', 'baz')).toBe(false) expect(state.things.filter('foo', 'bar', 'baz')).toEqual([foo]) - expect(state.things.find('foo', 'bar', 'baz')).toBe(foo) + + const _foo = state.things.find('foo', 'bar', 'baz') + expect(isReactive(_foo)).toBe(true) + expect(foo).toStrictEqual(_foo) + expect(state.things.findIndex('foo', 'bar', 'baz')).toBe(1) - expect(state.things.findLast('foo', 'bar', 'baz')).toBe(bar) + + const _bar = state.things.findLast('foo', 'bar', 'baz') + expect(isReactive(_bar)).toBe(true) + expect(bar).toStrictEqual(_bar) + expect(state.things.findLastIndex('foo', 'bar', 'baz')).toBe(1) expect(state.things.forEach('foo', 'bar', 'baz')).toBeUndefined() expect(state.things.map('foo', 'bar', 'baz')).toEqual(['1', '2', '3']) diff --git a/packages/reactivity/src/arrayInstrumentations.ts b/packages/reactivity/src/arrayInstrumentations.ts index 58c31835dfd..c0c09bf2e77 100644 --- a/packages/reactivity/src/arrayInstrumentations.ts +++ b/packages/reactivity/src/arrayInstrumentations.ts @@ -239,24 +239,23 @@ function apply( args?: IArguments, ) { const arr = shallowReadArray(self) + let needsWrap = arr !== self && !isShallow(self) + let methodFn // @ts-expect-error our code is limited to es2016 but user code is not if ((methodFn = arr[method]) !== arrayProto[method]) { - return methodFn.apply(arr, args) + const result = methodFn.apply(arr, args) + return needsWrap ? toReactive(result) : result } - let needsWrap = false let wrappedFn = fn - if (arr !== self) { - needsWrap = !isShallow(self) - if (needsWrap) { - wrappedFn = function (this: unknown, item, index) { - return fn.call(this, toReactive(item), index, self) - } - } else if (fn.length > 2) { - wrappedFn = function (this: unknown, item, index) { - return fn.call(this, item, index, self) - } + if (needsWrap) { + wrappedFn = function (this: unknown, item, index) { + return fn.call(this, toReactive(item), index, self) + } + } else if (fn.length > 2) { + wrappedFn = function (this: unknown, item, index) { + return fn.call(this, item, index, self) } } const result = methodFn.call(arr, wrappedFn, thisArg) From c32c53b1f526770abe93422a04bb964bdc354901 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 16 Aug 2024 09:13:47 +0800 Subject: [PATCH 3/6] chore: clean --- packages/reactivity/__tests__/reactiveArray.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/reactivity/__tests__/reactiveArray.spec.ts b/packages/reactivity/__tests__/reactiveArray.spec.ts index c04bf070f73..fabafc21e25 100644 --- a/packages/reactivity/__tests__/reactiveArray.spec.ts +++ b/packages/reactivity/__tests__/reactiveArray.spec.ts @@ -2,7 +2,6 @@ import { type ComputedRef, computed } from '../src/computed' import { isReactive, reactive, shallowReactive, toRaw } from '../src/reactive' import { isRef, ref } from '../src/ref' import { effect } from '../src/effect' -import { expect } from 'vitest' describe('reactivity/reactive/Array', () => { test('should make Array reactive', () => { From e6e6a27ee3c92eb781b56e0d3efe9a71947f302e Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 16 Aug 2024 09:21:44 +0800 Subject: [PATCH 4/6] chore: update --- .../reactivity/src/arrayInstrumentations.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/src/arrayInstrumentations.ts b/packages/reactivity/src/arrayInstrumentations.ts index c0c09bf2e77..c14c3dce12d 100644 --- a/packages/reactivity/src/arrayInstrumentations.ts +++ b/packages/reactivity/src/arrayInstrumentations.ts @@ -239,23 +239,25 @@ function apply( args?: IArguments, ) { const arr = shallowReadArray(self) - let needsWrap = arr !== self && !isShallow(self) - let methodFn // @ts-expect-error our code is limited to es2016 but user code is not if ((methodFn = arr[method]) !== arrayProto[method]) { const result = methodFn.apply(arr, args) - return needsWrap ? toReactive(result) : result + return arr !== self && !isShallow(self) ? toReactive(result) : result } + let needsWrap = false let wrappedFn = fn - if (needsWrap) { - wrappedFn = function (this: unknown, item, index) { - return fn.call(this, toReactive(item), index, self) - } - } else if (fn.length > 2) { - wrappedFn = function (this: unknown, item, index) { - return fn.call(this, item, index, self) + if (arr !== self) { + needsWrap = !isShallow(self) + if (needsWrap) { + wrappedFn = function (this: unknown, item, index) { + return fn.call(this, toReactive(item), index, self) + } + } else if (fn.length > 2) { + wrappedFn = function (this: unknown, item, index) { + return fn.call(this, item, index, self) + } } } const result = methodFn.call(arr, wrappedFn, thisArg) From 80764e8d5a7d0fe445f6e597e6089c51268037b8 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 16 Aug 2024 16:10:14 +0800 Subject: [PATCH 5/6] Revert "fix(types/custom-element): `defineCustomElement` with required props (#11578)" This reverts commit 77d5e88608d5c66f8bd5d87058e440b7115ec2bf. --- .../dts-test/defineCustomElement.test-d.ts | 33 ------------------- packages/runtime-dom/src/apiCustomElement.ts | 8 ++--- 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/packages/dts-test/defineCustomElement.test-d.ts b/packages/dts-test/defineCustomElement.test-d.ts index f81f8b8fa61..b81c0befe9f 100644 --- a/packages/dts-test/defineCustomElement.test-d.ts +++ b/packages/dts-test/defineCustomElement.test-d.ts @@ -99,37 +99,4 @@ describe('defineCustomElement using defineComponent return type', () => { expectType(instance.a) instance.a = 42 }) - - test('with required props', () => { - const Comp1Vue = defineComponent({ - props: { - a: { type: Number, required: true }, - }, - }) - const Comp = defineCustomElement(Comp1Vue) - expectType(Comp) - - const instance = new Comp() - expectType(instance.a) - instance.a = 42 - }) - - test('with default props', () => { - const Comp1Vue = defineComponent({ - props: { - a: { - type: Number, - default: 1, - validator: () => true, - }, - }, - emits: ['click'], - }) - const Comp = defineCustomElement(Comp1Vue) - expectType(Comp) - - const instance = new Comp() - expectType(instance.a) - instance.a = 42 - }) }) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 2b472e73027..efee4d8a9c1 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -9,7 +9,6 @@ import { type ComponentOptionsBase, type ComponentOptionsMixin, type ComponentProvideOptions, - type ComponentPublicInstance, type ComputedOptions, type ConcreteComponent, type CreateAppFunction, @@ -154,13 +153,14 @@ export function defineCustomElement< // overload 3: defining a custom element from the returned value of // `defineComponent` export function defineCustomElement< - // this should be `ComponentPublicInstanceConstructor` but that type is not exported - T extends { new (...args: any[]): ComponentPublicInstance }, + T extends DefineComponent, >( options: T, extraOptions?: CustomElementOptions, ): VueElementConstructor< - T extends DefineComponent ? P : unknown + T extends DefineComponent + ? ExtractPropTypes

+ : unknown > /*! #__NO_SIDE_EFFECTS__ */ From 4ca5de72f2385cc6465202ad4deaeb6324eed060 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 16 Aug 2024 16:19:26 +0800 Subject: [PATCH 6/6] chore: minor tweaks --- packages/reactivity/src/arrayInstrumentations.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/reactivity/src/arrayInstrumentations.ts b/packages/reactivity/src/arrayInstrumentations.ts index c14c3dce12d..6cf7602eca5 100644 --- a/packages/reactivity/src/arrayInstrumentations.ts +++ b/packages/reactivity/src/arrayInstrumentations.ts @@ -239,17 +239,17 @@ function apply( args?: IArguments, ) { const arr = shallowReadArray(self) - let methodFn + const needsWrap = arr !== self && !isShallow(self) // @ts-expect-error our code is limited to es2016 but user code is not - if ((methodFn = arr[method]) !== arrayProto[method]) { + const methodFn = arr[method] + // @ts-expect-error + if (methodFn !== arrayProto[method]) { const result = methodFn.apply(arr, args) - return arr !== self && !isShallow(self) ? toReactive(result) : result + return needsWrap ? toReactive(result) : result } - let needsWrap = false let wrappedFn = fn if (arr !== self) { - needsWrap = !isShallow(self) if (needsWrap) { wrappedFn = function (this: unknown, item, index) { return fn.call(this, toReactive(item), index, self)