From 4813c9b118771e5a49e40e0dda3c3a69e8e5b072 Mon Sep 17 00:00:00 2001 From: Carlos Rodrigues Date: Fri, 11 Feb 2022 15:17:03 +0000 Subject: [PATCH 1/3] types(runtime-core): ComponentInstance type --- packages/runtime-core/src/component.ts | 21 +++++ packages/runtime-core/src/index.ts | 3 +- test-dts/componentInstance.test-d.tsx | 107 +++++++++++++++++++++++++ test-dts/defineComponent.test-d.tsx | 7 +- 4 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 test-dts/componentInstance.test-d.tsx diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 9b2f35c9745..d343b7e5054 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -70,6 +70,27 @@ import { SchedulerJob } from './scheduler' export type Data = Record +export type ComponentInstance = T extends { new (): ComponentPublicInstance } + ? InstanceType + : T extends FunctionalComponent + ? ComponentPublicInstance + : T extends Component< + infer Props, + infer RawBindings, + infer D, + infer C, + infer M + > + ? // NOTE we override Props/RawBindings/D to make sure is not `unknown` + ComponentPublicInstance< + unknown extends Props ? {} : Props, + unknown extends RawBindings ? {} : RawBindings, + unknown extends D ? {} : D, + C, + M + > + : never // not a vue Component + /** * For extending allowed non-declared props on components in TSX */ diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index ad4817d91b9..6f98e3f9e56 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -206,7 +206,8 @@ export { ComponentInternalInstance, SetupContext, ComponentCustomProps, - AllowedComponentProps + AllowedComponentProps, + ComponentInstance } from './component' export { DefineComponent } from './apiDefineComponent' export { diff --git a/test-dts/componentInstance.test-d.tsx b/test-dts/componentInstance.test-d.tsx new file mode 100644 index 00000000000..0d4a7df7100 --- /dev/null +++ b/test-dts/componentInstance.test-d.tsx @@ -0,0 +1,107 @@ +import { + defineComponent, + expectType, + FunctionalComponent, + ComponentPublicInstance, + ComponentInstance +} from './index' + +const CompSetup = defineComponent({ + props: { + test: String + }, + setup() { + return { + a: 1 + } + } +}) +declare const compSetup: ComponentInstance + +expectType(compSetup.test) +expectType(compSetup.a) +expectType(compSetup) + +// Functional +declare const CompFunctional: FunctionalComponent<{ test?: string }> +declare const compFunctional: ComponentInstance + +expectType(compFunctional.test) +expectType(compFunctional) + +declare const CompFunction: (props: { test?: string }) => any +declare const compFunction: ComponentInstance + +expectType(compFunction.test) +expectType(compFunction) + +// Options +const CompOptions = defineComponent({ + props: { + test: String + }, + data() { + return { + a: 1 + } + }, + computed: { + b() { + return 'test' + } + }, + methods: { + func(a: string) { + return true + } + } +}) +declare const compOptions: ComponentInstance +expectType(compOptions.test) +expectType(compOptions.a) +expectType<(a: string) => boolean>(compOptions.func) +expectType(compOptions) + +// object - no defineComponent + +const CompObjectSetup = { + props: { + test: String + }, + setup() { + return { + a: 1 + } + } +} +declare const compObjectSetup: ComponentInstance +expectType(compObjectSetup.test) +expectType(compObjectSetup.a) +expectType(compObjectSetup) + +const CompObjectData = { + props: { + test: String + }, + data() { + return { + a: 1 + } + } +} +declare const compObjectData: ComponentInstance +expectType(compObjectData.test) +expectType(compObjectData.a) +expectType(compObjectData) + +const CompObjectNoProps = { + data() { + return { + a: 1 + } + } +} +declare const compObjectNoProps: ComponentInstance +expectType(compObjectNoProps.test) +expectType(compObjectNoProps.a) +expectType(compObjectNoProps) diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index 031b4a13eb8..505802d202c 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -12,7 +12,8 @@ import { ComponentOptions, SetupContext, IsUnion, - h + h, + ComponentInstance } from './index' describe('with object props', () => { @@ -1066,7 +1067,7 @@ describe('extract instance type', () => { } }) - const compA = {} as InstanceType + const compA = {} as ComponentInstance expectType(compA.a) expectType(compA.b) @@ -1114,7 +1115,7 @@ describe('async setup', () => { } }) - const vm = {} as InstanceType + const vm = {} as ComponentInstance // assert setup context unwrapping expectType(vm.a) expectType(vm.b.c.value) From 05cea29383289e52dcc1e1185d760747808f5861 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 6 May 2022 05:04:21 -0400 Subject: [PATCH 2/3] Update component.ts --- packages/runtime-core/src/component.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index d343b7e5054..f50b11730c8 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -70,6 +70,18 @@ import { SchedulerJob } from './scheduler' export type Data = Record +/** + * Public utility type for extracting the instance type of a component. + * Works with all valid component definition types. This is intended to replace + * the usage of `InstanceType` which only works for + * constructor-based component definition types. + * + * Exmaple: + * ```ts + * const MyComp = { ... } + * declare const instance: ComponentInstance + * ``` + */ export type ComponentInstance = T extends { new (): ComponentPublicInstance } ? InstanceType : T extends FunctionalComponent From 0b63443d9b4617f656b33f3f6153ac956bec43c1 Mon Sep 17 00:00:00 2001 From: pikax Date: Fri, 6 Oct 2023 15:02:22 +0100 Subject: [PATCH 3/3] tests --- .../dts-test/componentInstance.test-d.tsx | 198 ++++++++++-------- 1 file changed, 115 insertions(+), 83 deletions(-) diff --git a/packages/dts-test/componentInstance.test-d.tsx b/packages/dts-test/componentInstance.test-d.tsx index d8a8ef1a438..2fb20312b2c 100644 --- a/packages/dts-test/componentInstance.test-d.tsx +++ b/packages/dts-test/componentInstance.test-d.tsx @@ -2,106 +2,138 @@ import { defineComponent, FunctionalComponent, ComponentPublicInstance, - ComponentInstance + ComponentInstance, + ref } from 'vue' -import { expectType } from './utils' +import { expectType, describe } from './utils' -const CompSetup = defineComponent({ - props: { - test: String - }, - setup() { - return { - a: 1 +describe('defineComponent', () => { + const CompSetup = defineComponent({ + props: { + test: String + }, + setup() { + return { + a: 1 + } } - } -}) -declare const compSetup: ComponentInstance + }) + const compSetup: ComponentInstance = {} as any -expectType(compSetup.test) -expectType(compSetup.a) -expectType(compSetup) - -// Functional -declare const CompFunctional: FunctionalComponent<{ test?: string }> -declare const compFunctional: ComponentInstance + expectType(compSetup.test) + expectType(compSetup.a) + expectType(compSetup) +}) +describe('functional component', () => { + // Functional + const CompFunctional: FunctionalComponent<{ test?: string }> = {} as any + const compFunctional: ComponentInstance = {} as any -expectType(compFunctional.test) -expectType(compFunctional) + expectType(compFunctional.test) + expectType(compFunctional) -declare const CompFunction: (props: { test?: string }) => any -declare const compFunction: ComponentInstance + const CompFunction: (props: { test?: string }) => any = {} as any + const compFunction: ComponentInstance = {} as any -expectType(compFunction.test) -expectType(compFunction) + expectType(compFunction.test) + expectType(compFunction) +}) -// Options -const CompOptions = defineComponent({ - props: { - test: String - }, - data() { - return { - a: 1 - } - }, - computed: { - b() { - return 'test' - } - }, - methods: { - func(a: string) { - return true +describe('options component', () => { + // Options + const CompOptions = defineComponent({ + props: { + test: String + }, + data() { + return { + a: 1 + } + }, + computed: { + b() { + return 'test' + } + }, + methods: { + func(a: string) { + return true + } } - } + }) + const compOptions: ComponentInstance = {} as any + expectType(compOptions.test) + expectType(compOptions.a) + expectType<(a: string) => boolean>(compOptions.func) + expectType(compOptions) }) -declare const compOptions: ComponentInstance -expectType(compOptions.test) -expectType(compOptions.a) -expectType<(a: string) => boolean>(compOptions.func) -expectType(compOptions) -// object - no defineComponent +describe('object no defineComponent', () => { + // object - no defineComponent -const CompObjectSetup = { - props: { - test: String - }, - setup() { - return { - a: 1 + const CompObjectSetup = { + props: { + test: String + }, + setup() { + return { + a: 1 + } } } -} -declare const compObjectSetup: ComponentInstance -expectType(compObjectSetup.test) -expectType(compObjectSetup.a) -expectType(compObjectSetup) + const compObjectSetup: ComponentInstance = {} as any + expectType(compObjectSetup.test) + expectType(compObjectSetup.a) + expectType(compObjectSetup) -const CompObjectData = { - props: { - test: String - }, - data() { - return { - a: 1 + const CompObjectData = { + props: { + test: String + }, + data() { + return { + a: 1 + } } } -} -declare const compObjectData: ComponentInstance -expectType(compObjectData.test) -expectType(compObjectData.a) -expectType(compObjectData) + const compObjectData: ComponentInstance = {} as any + expectType(compObjectData.test) + expectType(compObjectData.a) + expectType(compObjectData) -const CompObjectNoProps = { - data() { - return { - a: 1 + const CompObjectNoProps = { + data() { + return { + a: 1 + } } } -} -declare const compObjectNoProps: ComponentInstance -expectType(compObjectNoProps.test) -expectType(compObjectNoProps.a) -expectType(compObjectNoProps) + const compObjectNoProps: ComponentInstance = + {} as any + expectType(compObjectNoProps.test) + expectType(compObjectNoProps.a) + expectType(compObjectNoProps) +}) + +describe('Generic component', () => { + const Comp = defineComponent( + // TODO: babel plugin to auto infer runtime props options from type + // similar to defineProps<{...}>() + (props: { msg: T; list: T[] }) => { + // use Composition API here like in