]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(types): support inferring injected properties in options api (#6804)
authorRudy <xuhaodong66@foxmail.com>
Tue, 8 Nov 2022 06:09:53 +0000 (14:09 +0800)
committerGitHub <noreply@github.com>
Tue, 8 Nov 2022 06:09:53 +0000 (01:09 -0500)
close #3031
close #5931

packages/runtime-core/src/apiDefineComponent.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/componentPublicInstance.ts
packages/runtime-core/src/index.ts
packages/runtime-dom/src/apiCustomElement.ts
test-dts/defineComponent.test-d.tsx
test-dts/defineCustomElement.test-d.ts [new file with mode: 0644]

index c9c3ab61e8504e6911862bcc965ffa8ed5ffce34..10f4f69acab6989723b73a88fa1f6783596f01be 100644 (file)
@@ -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<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>
 
@@ -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<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
 
index 36ba0d5166a14a78181fb8bac3584b09371ce9f7..5275a747db9352a0b79d73ea1e4ce1491bd81009 100644 (file)
@@ -118,8 +118,10 @@ export interface ComponentOptionsBase<
   Extends extends ComponentOptionsMixin,
   E extends EmitsOptions,
   EE extends string = string,
-  Defaults = {}
-> extends LegacyOptions<Props, D, C, M, Mixin, Extends>,
+  Defaults = {},
+  I extends ComponentInjectOptions = {},
+  II extends string = string
+> extends LegacyOptions<Props, D, C, M, Mixin, Extends, I, II>,
     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<E>
+  I extends ComponentInjectOptions = {},
+  II extends string = string,
+  PE = Props & EmitsToProps<E>,
 > = ComponentOptionsBase<
   PE,
   RawBindings,
@@ -236,11 +240,13 @@ export type ComponentOptionsWithoutProps<
   Extends,
   E,
   EE,
-  {}
+  {},
+  I,
+  II
 > & {
   props?: undefined
 } & ThisType<
-    CreateComponentPublicInstance<PE, RawBindings, D, C, M, Mixin, Extends, E>
+    CreateComponentPublicInstance<PE, RawBindings, D, C, M, Mixin, Extends, E, PE, {}, false, I>
   >
 
 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<E>
 > = 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<ExtractPropTypes<PropsOptions>> & EmitsToProps<E>,
-  Defaults = ExtractDefaultPropTypes<PropsOptions>
+  Defaults = ExtractDefaultPropTypes<PropsOptions>,
 > = ComponentOptionsBase<
   Props,
   RawBindings,
@@ -302,7 +318,9 @@ export type ComponentOptionsWithObjectProps<
   Extends,
   E,
   EE,
-  Defaults
+  Defaults,
+  I,
+  II
 > & {
   props: PropsOptions & ThisType<void>
 } & 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<string | symbol, unknown>
 
-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 ComponentInjectOptions> = 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<string, Function>
index 1e4c83c96d89bf1c5197587ce8f73839e267ad16..4d6f1d059166ae36db77371f485de063c3f153b9 100644 (file)
@@ -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<Mixin> & IntersectionMixin<Extends>,
   PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
   PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
@@ -150,7 +153,7 @@ export type CreateComponentPublicInstance<
   PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
     EnsureNonVoid<M>,
   PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
-    EnsureNonVoid<Defaults>
+    EnsureNonVoid<Defaults>,
 > = ComponentPublicInstance<
   PublicP,
   PublicB,
@@ -161,7 +164,8 @@ export type CreateComponentPublicInstance<
   PublicProps,
   PublicDefaults,
   MakeDefaultsOptional,
-  ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>
+  ComponentOptionsBase<P, B, D, C, M, Mixin, Extends, E, string, Defaults>,
+  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<any, any, any, any, any, any, any, any, any>
+  Options = ComponentOptionsBase<any, any, any, any, any, any, any, any, any>,
+  I extends ComponentInjectOptions = {}
 > = {
   $: ComponentInternalInstance
   $data: D
@@ -205,7 +210,8 @@ export type ComponentPublicInstance<
   UnwrapNestedRefs<D> &
   ExtractComputedReturns<C> &
   M &
-  ComponentCustomProperties
+  ComponentCustomProperties &
+  InjectToObject<I>
 
 export type PublicPropertiesMap = Record<
   string,
index 2b9bde82c44c30d5dae69d4cf1dd94c5de74603f..7f822489bdc7ecfe843ed876498fe7227d2778a2 100644 (file)
@@ -222,7 +222,8 @@ export {
   RenderFunction,
   MethodOptions,
   ComputedOptions,
-  RuntimeCompilerOptions
+  RuntimeCompilerOptions,
+  ComponentInjectOptions
 } from './componentOptions'
 export { EmitsOptions, ObjectEmitsOptions } from './componentEmits'
 export {
index 32beffe126acca7ea647012fadb2201b9551ba5a..5108e3bdfbe82ead778cf0aaed7b9dc221e1d9c3 100644 (file)
@@ -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<Props>
 
@@ -74,7 +79,9 @@ export function defineCustomElement<
   Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
   Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
   E extends EmitsOptions = Record<string, any>,
-  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<string, any>,
-  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<ExtractPropTypes<PropsOptions>>
 
index e3e0f9ebc26912cc664a2888e8c485957b9788e0..208501e4286204e0c9b2b10b874f3812fcd857fc 100644 (file)
@@ -1033,6 +1033,68 @@ describe('emits', () => {
   })
 })
 
+describe('inject', () => {
+  // with object inject
+  defineComponent({
+    props: {
+      a: String
+    },
+    inject: {
+      foo: 'foo',
+      bar: 'bar',
+    },
+    created() {
+      expectType<unknown>(this.foo)
+      expectType<unknown>(this.bar)
+      //  @ts-expect-error
+      expectError(this.foobar = 1)
+    }
+  })
+
+  // with array inject
+  defineComponent({
+    props: ['a', 'b'],
+    inject: ['foo', 'bar'],
+    created() {
+      expectType<unknown>(this.foo)
+      expectType<unknown>(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<unknown>(this.foo)
+      expectType<unknown>(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<ComponentOptions['setup']>(
     {} as (props: Record<string, any>, ctx: SetupContext) => any
diff --git a/test-dts/defineCustomElement.test-d.ts b/test-dts/defineCustomElement.test-d.ts
new file mode 100644 (file)
index 0000000..8e60ac1
--- /dev/null
@@ -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<unknown>(this.foo)
+      expectType<unknown>(this.bar)
+      //  @ts-expect-error
+      expectError(this.foobar = 1)
+    }
+  })
+
+  // with array inject
+  defineCustomElement({
+    props: ['a', 'b'],
+    inject: ['foo', 'bar'],
+    created() {
+      expectType<unknown>(this.foo)
+      expectType<unknown>(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<unknown>(this.foo)
+      expectType<unknown>(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