From e4de623ea7289665ac8e827d8aa4783de1d6d380 Mon Sep 17 00:00:00 2001 From: Rudy Date: Tue, 8 Nov 2022 14:09:53 +0800 Subject: [PATCH] fix(types): support inferring injected properties in options api (#6804) close #3031 close #5931 --- .../runtime-core/src/apiDefineComponent.ts | 27 +++++--- packages/runtime-core/src/componentOptions.ts | 57 +++++++++++++---- .../src/componentPublicInstance.ts | 16 +++-- packages/runtime-core/src/index.ts | 3 +- packages/runtime-dom/src/apiCustomElement.ts | 27 +++++--- test-dts/defineComponent.test-d.tsx | 62 ++++++++++++++++++ test-dts/defineCustomElement.test-d.ts | 63 +++++++++++++++++++ 7 files changed, 222 insertions(+), 33 deletions(-) create mode 100644 test-dts/defineCustomElement.test-d.ts diff --git a/packages/runtime-core/src/apiDefineComponent.ts b/packages/runtime-core/src/apiDefineComponent.ts index c9c3ab61e85..10f4f69acab 100644 --- a/packages/runtime-core/src/apiDefineComponent.ts +++ b/packages/runtime-core/src/apiDefineComponent.ts @@ -6,7 +6,8 @@ import { ComponentOptionsWithObjectProps, ComponentOptionsMixin, RenderFunction, - ComponentOptionsBase + ComponentOptionsBase, + ComponentInjectOptions } from './componentOptions' import { SetupContext, @@ -104,7 +105,9 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, - EE extends string = string + EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string >( options: ComponentOptionsWithoutProps< Props, @@ -115,7 +118,9 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + I, + II > ): DefineComponent @@ -131,7 +136,9 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, - EE extends string = string + EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string, >( options: ComponentOptionsWithArrayProps< PropNames, @@ -142,7 +149,9 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + I, + II > ): DefineComponent< Readonly<{ [key in PropNames]?: any }>, @@ -169,7 +178,9 @@ export function defineComponent< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, - EE extends string = string + EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string, >( options: ComponentOptionsWithObjectProps< PropsOptions, @@ -180,7 +191,9 @@ export function defineComponent< Mixin, Extends, E, - EE + EE, + I, + II > ): DefineComponent diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts index 36ba0d5166a..5275a747db9 100644 --- a/packages/runtime-core/src/componentOptions.ts +++ b/packages/runtime-core/src/componentOptions.ts @@ -118,8 +118,10 @@ export interface ComponentOptionsBase< Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, - Defaults = {} -> extends LegacyOptions, + Defaults = {}, + I extends ComponentInjectOptions = {}, + II extends string = string +> extends LegacyOptions, ComponentInternalOptions, ComponentCustomOptions { setup?: ( @@ -225,7 +227,9 @@ export type ComponentOptionsWithoutProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, - PE = Props & EmitsToProps + I extends ComponentInjectOptions = {}, + II extends string = string, + PE = Props & EmitsToProps, > = ComponentOptionsBase< PE, RawBindings, @@ -236,11 +240,13 @@ export type ComponentOptionsWithoutProps< Extends, E, EE, - {} + {}, + I, + II > & { props?: undefined } & ThisType< - CreateComponentPublicInstance + CreateComponentPublicInstance > export type ComponentOptionsWithArrayProps< @@ -253,6 +259,8 @@ export type ComponentOptionsWithArrayProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string, Props = Readonly<{ [key in PropNames]?: any }> & EmitsToProps > = ComponentOptionsBase< Props, @@ -264,7 +272,9 @@ export type ComponentOptionsWithArrayProps< Extends, E, EE, - {} + {}, + I, + II > & { props: PropNames[] } & ThisType< @@ -276,7 +286,11 @@ export type ComponentOptionsWithArrayProps< M, Mixin, Extends, - E + E, + Props, + {}, + false, + I > > @@ -290,8 +304,10 @@ export type ComponentOptionsWithObjectProps< Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string, Props = Readonly> & EmitsToProps, - Defaults = ExtractDefaultPropTypes + Defaults = ExtractDefaultPropTypes, > = ComponentOptionsBase< Props, RawBindings, @@ -302,7 +318,9 @@ export type ComponentOptionsWithObjectProps< Extends, E, EE, - Defaults + Defaults, + I, + II > & { props: PropsOptions & ThisType } & ThisType< @@ -317,7 +335,8 @@ export type ComponentOptionsWithObjectProps< E, Props, Defaults, - false + false, + I > > @@ -389,20 +408,32 @@ export type ComponentProvideOptions = ObjectProvideOptions | Function type ObjectProvideOptions = Record -type ComponentInjectOptions = string[] | ObjectInjectOptions +export type ComponentInjectOptions = string[] | ObjectInjectOptions type ObjectInjectOptions = Record< string | symbol, string | symbol | { from?: string | symbol; default?: unknown } > +export type InjectToObject = T extends string[] +? { + [K in T[number]]?: unknown +} +: T extends ObjectInjectOptions +? { + [K in keyof T]?: unknown +} +: never + interface LegacyOptions< Props, D, C extends ComputedOptions, M extends MethodOptions, Mixin extends ComponentOptionsMixin, - Extends extends ComponentOptionsMixin + Extends extends ComponentOptionsMixin, + I extends ComponentInjectOptions, + II extends string > { compatConfig?: CompatConfig @@ -437,7 +468,7 @@ interface LegacyOptions< methods?: M watch?: ComponentWatchOptions provide?: ComponentProvideOptions - inject?: ComponentInjectOptions + inject?: I | II[] // assets filters?: Record diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts index 1e4c83c96d8..4d6f1d05916 100644 --- a/packages/runtime-core/src/componentPublicInstance.ts +++ b/packages/runtime-core/src/componentPublicInstance.ts @@ -34,7 +34,9 @@ import { OptionTypesKeys, resolveMergedOptions, shouldCacheAccess, - MergedComponentOptionsOverride + MergedComponentOptionsOverride, + InjectToObject, + ComponentInjectOptions } from './componentOptions' import { EmitsOptions, EmitFn } from './componentEmits' import { Slots } from './componentSlots' @@ -141,6 +143,7 @@ export type CreateComponentPublicInstance< PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, + I extends ComponentInjectOptions = {}, PublicMixin = IntersectionMixin & IntersectionMixin, PublicP = UnwrapMixinsType & EnsureNonVoid

, PublicB = UnwrapMixinsType & EnsureNonVoid, @@ -150,7 +153,7 @@ export type CreateComponentPublicInstance< PublicM extends MethodOptions = UnwrapMixinsType & EnsureNonVoid, PublicDefaults = UnwrapMixinsType & - EnsureNonVoid + EnsureNonVoid, > = ComponentPublicInstance< PublicP, PublicB, @@ -161,7 +164,8 @@ export type CreateComponentPublicInstance< PublicProps, PublicDefaults, MakeDefaultsOptional, - ComponentOptionsBase + ComponentOptionsBase, + I > // public properties exposed on the proxy, which is used as the render context @@ -176,7 +180,8 @@ export type ComponentPublicInstance< PublicProps = P, Defaults = {}, MakeDefaultsOptional extends boolean = false, - Options = ComponentOptionsBase + Options = ComponentOptionsBase, + I extends ComponentInjectOptions = {} > = { $: ComponentInternalInstance $data: D @@ -205,7 +210,8 @@ export type ComponentPublicInstance< UnwrapNestedRefs & ExtractComputedReturns & M & - ComponentCustomProperties + ComponentCustomProperties & + InjectToObject export type PublicPropertiesMap = Record< string, diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 2b9bde82c44..7f822489bdc 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -222,7 +222,8 @@ export { RenderFunction, MethodOptions, ComputedOptions, - RuntimeCompilerOptions + RuntimeCompilerOptions, + ComponentInjectOptions } from './componentOptions' export { EmitsOptions, ObjectEmitsOptions } from './componentEmits' export { diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 32beffe126a..5108e3bdfbe 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -19,7 +19,8 @@ import { nextTick, warn, ConcreteComponent, - ComponentOptions + ComponentOptions, + ComponentInjectOptions } from '@vue/runtime-core' import { camelize, extend, hyphenate, isArray, toNumber } from '@vue/shared' import { hydrate, render } from '.' @@ -49,7 +50,9 @@ export function defineCustomElement< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = EmitsOptions, - EE extends string = string + EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string >( options: ComponentOptionsWithoutProps< Props, @@ -60,7 +63,9 @@ export function defineCustomElement< Mixin, Extends, E, - EE + EE, + I, + II > & { styles?: string[] } ): VueElementConstructor @@ -74,7 +79,9 @@ export function defineCustomElement< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, - EE extends string = string + EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string >( options: ComponentOptionsWithArrayProps< PropNames, @@ -85,7 +92,9 @@ export function defineCustomElement< Mixin, Extends, E, - EE + EE, + I, + II > & { styles?: string[] } ): VueElementConstructor<{ [K in PropNames]: any }> @@ -99,7 +108,9 @@ export function defineCustomElement< Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = Record, - EE extends string = string + EE extends string = string, + I extends ComponentInjectOptions = {}, + II extends string = string >( options: ComponentOptionsWithObjectProps< PropsOptions, @@ -110,7 +121,9 @@ export function defineCustomElement< Mixin, Extends, E, - EE + EE, + I, + II > & { styles?: string[] } ): VueElementConstructor> diff --git a/test-dts/defineComponent.test-d.tsx b/test-dts/defineComponent.test-d.tsx index e3e0f9ebc26..208501e4286 100644 --- a/test-dts/defineComponent.test-d.tsx +++ b/test-dts/defineComponent.test-d.tsx @@ -1033,6 +1033,68 @@ describe('emits', () => { }) }) +describe('inject', () => { + // with object inject + defineComponent({ + props: { + a: String + }, + inject: { + foo: 'foo', + bar: 'bar', + }, + created() { + expectType(this.foo) + expectType(this.bar) + // @ts-expect-error + expectError(this.foobar = 1) + } + }) + + // with array inject + defineComponent({ + props: ['a', 'b'], + inject: ['foo', 'bar'], + created() { + expectType(this.foo) + expectType(this.bar) + // @ts-expect-error + expectError(this.foobar = 1) + } + }) + + // with no props + defineComponent({ + inject: { + foo: { + from: 'pfoo', + default: 'foo' + }, + bar: { + from: 'pbar', + default: 'bar' + }, + }, + created() { + expectType(this.foo) + expectType(this.bar) + // @ts-expect-error + expectError(this.foobar = 1) + } + }) + + // without inject + defineComponent({ + props: ['a', 'b'], + created() { + // @ts-expect-error + expectError(this.foo = 1) + // @ts-expect-error + expectError(this.bar = 1) + } + }) +}) + describe('componentOptions setup should be `SetupContext`', () => { expectType( {} as (props: Record, ctx: SetupContext) => any diff --git a/test-dts/defineCustomElement.test-d.ts b/test-dts/defineCustomElement.test-d.ts new file mode 100644 index 00000000000..8e60ac1c1df --- /dev/null +++ b/test-dts/defineCustomElement.test-d.ts @@ -0,0 +1,63 @@ +import { defineCustomElement, expectType, expectError } from './index' + +describe('inject', () => { + // with object inject + defineCustomElement({ + props: { + a: String + }, + inject: { + foo: 'foo', + bar: 'bar', + }, + created() { + expectType(this.foo) + expectType(this.bar) + // @ts-expect-error + expectError(this.foobar = 1) + } + }) + + // with array inject + defineCustomElement({ + props: ['a', 'b'], + inject: ['foo', 'bar'], + created() { + expectType(this.foo) + expectType(this.bar) + // @ts-expect-error + expectError(this.foobar = 1) + } + }) + + // with no props + defineCustomElement({ + inject: { + foo: { + from: 'pbar', + default: 'foo' + }, + bar: { + from: 'pfoo', + default: 'bar' + }, + }, + created() { + expectType(this.foo) + expectType(this.bar) + // @ts-expect-error + expectError(this.foobar = 1) + } + }) + + // without inject + defineCustomElement({ + props: ['a', 'b'], + created() { + // @ts-expect-error + expectError(this.foo = 1) + // @ts-expect-error + expectError(this.bar = 1) + } + }) +}) \ No newline at end of file