]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
types: mixin and extends typing on data and setup (#2404)
authorCarlos Rodrigues <david-181@hotmail.com>
Fri, 4 Dec 2020 21:06:34 +0000 (21:06 +0000)
committerGitHub <noreply@github.com>
Fri, 4 Dec 2020 21:06:34 +0000 (16:06 -0500)
close #2350

packages/runtime-core/src/componentOptions.ts
test-dts/defineComponent.test-d.tsx

index 76e61a44ed4f0d86e27e2b8f3aa04b563ccd3ea1..2017b98f6afe2b84a80a6c429da317f6e1c410ac 100644 (file)
@@ -59,6 +59,7 @@ import {
 import { warn } from './warning'
 import { VNodeChild } from './vnode'
 import { callWithAsyncErrorHandling } from './errorHandling'
+import { UnionToIntersection } from './helpers/typeUtils'
 
 /**
  * Interface for declaring custom options.
@@ -80,6 +81,19 @@ export interface ComponentCustomOptions {}
 
 export type RenderFunction = () => VNodeChild
 
+type ExtractOptionProp<T> = T extends ComponentOptionsBase<
+  infer P,
+  any,
+  any,
+  any,
+  any,
+  any,
+  any,
+  any
+>
+  ? unknown extends P ? {} : P
+  : {}
+
 export interface ComponentOptionsBase<
   Props,
   RawBindings,
@@ -97,7 +111,9 @@ export interface ComponentOptionsBase<
     ComponentCustomOptions {
   setup?: (
     this: void,
-    props: Props,
+    props: Props &
+      UnionToIntersection<ExtractOptionProp<Mixin>> &
+      UnionToIntersection<ExtractOptionProp<Extends>>,
     ctx: SetupContext<E>
   ) => Promise<RawBindings> | RawBindings | RenderFunction | void
   name?: string
@@ -358,8 +374,24 @@ interface LegacyOptions<
   // since that leads to some sort of circular inference and breaks ThisType
   // for the entire component.
   data?: (
-    this: CreateComponentPublicInstance<Props>,
-    vm: CreateComponentPublicInstance<Props>
+    this: CreateComponentPublicInstance<
+      Props,
+      {},
+      {},
+      {},
+      MethodOptions,
+      Mixin,
+      Extends
+    >,
+    vm: CreateComponentPublicInstance<
+      Props,
+      {},
+      {},
+      {},
+      MethodOptions,
+      Mixin,
+      Extends
+    >
   ) => D
   computed?: C
   methods?: M
@@ -590,6 +622,7 @@ export function applyOptions(
       deferredData.forEach(dataFn => resolveData(instance, dataFn, publicThis))
     }
     if (dataOptions) {
+      // @ts-ignore dataOptions is not fully type safe
       resolveData(instance, dataOptions, publicThis)
     }
     if (__DEV__) {
index c77feba025d37c1160766e01d6f469499b3a0184..e28a82e1d37355347b66a8b81ebb364b3db08883 100644 (file)
@@ -445,10 +445,18 @@ describe('with mixins', () => {
   const MixinD = defineComponent({
     mixins: [MixinA],
     data() {
+      //@ts-expect-error computed are not available on data()
+      expectError<number>(this.dC1)
+      //@ts-expect-error computed are not available on data()
+      expectError<string>(this.dC2)
+
       return {
         d: 4
       }
     },
+    setup(props) {
+      expectType<string>(props.aP1)
+    },
     computed: {
       dC1(): number {
         return this.d + this.a
@@ -467,6 +475,34 @@ describe('with mixins', () => {
         required: true
       }
     },
+
+    data(vm) {
+      expectType<number>(vm.a)
+      expectType<number>(vm.b)
+      expectType<number>(vm.c)
+      expectType<number>(vm.d)
+
+      // should also expose declared props on `this`
+      expectType<number>(this.a)
+      expectType<string>(this.aP1)
+      expectType<boolean | undefined>(this.aP2)
+      expectType<number>(this.b)
+      expectType<any>(this.bP1)
+      expectType<number>(this.c)
+      expectType<number>(this.d)
+
+      return {}
+    },
+
+    setup(props) {
+      expectType<string>(props.z)
+      // props
+      expectType<string>(props.aP1)
+      expectType<boolean | undefined>(props.aP2)
+      expectType<any>(props.bP1)
+      expectType<any>(props.bP2)
+      expectType<string>(props.z)
+    },
     render() {
       const props = this.$props
       // props