Repository: vuejs/vue
Pull request: fix(types): correct scopedSlot types

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

**Other information:**
see #8946
- scoped slots can return a single VNode if `<template>` is not used
- `$scopedSlots` needs to be able to be passed down to children (https://github.com/vuetifyjs/vuetify/blob/8a1b48304b64ff001e9feda3c64a23883448d76b/packages/vuetify/src/components/VTreeview/VTreeviewNode.ts#L262)

Changed files:
filename: types/test/options-test.ts
patch: @@ -1,5 +1,5 @@
 import Vue, { VNode } from "../index";
-import { AsyncComponent, ComponentOptions, FunctionalComponentOptions, Component } from "../index";
+import { ComponentOptions, Component } from "../index";
 import { CreateElement } from "../vue";
 
 interface MyComponent extends Vue {
@@ -297,6 +297,10 @@ Vue.component('component-with-scoped-slot', {
           // named scoped slot as vnode data
           item: (props: ScopedSlotProps) => [h('span', [props.msg])]
         }
+      }),
+      h('child', {
+        // Passing down all slots from parent
+        scopedSlots: this.$scopedSlots
       })
     ])
   },
@@ -315,13 +319,18 @@ Vue.component('component-with-scoped-slot', {
 Vue.component('narrow-array-of-vnode-type', {
   render (h): VNode {
     const slot = this.$scopedSlots.default!({})
-    if (typeof slot !== 'string') {
+    if (typeof slot === 'string') {
+      return h('span', slot)
+    } else if (Array.isArray(slot)) {
       const first = slot[0]
       if (!Array.isArray(first) && typeof first !== 'string') {
-        return first;
+        return first
+      } else {
+        return h()
       }
+    } else {
+      return slot
     }
-    return h();
   }
 })
 

filename: types/vnode.d.ts
patch: @@ -1,6 +1,6 @@
 import { Vue } from "./vue";
 
-export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | string;
+export type ScopedSlot = (props: any) => VNodeChildrenArrayContents | VNode | string;
 
 export type VNodeChildren = VNodeChildrenArrayContents | [ScopedSlot] | string;
 export interface VNodeChildrenArrayContents extends Array<VNode | string | VNodeChildrenArrayContents> {}
@@ -34,7 +34,7 @@ export interface VNodeComponentOptions {
 export interface VNodeData {
   key?: string | number;
   slot?: string;
-  scopedSlots?: { [key: string]: ScopedSlot };
+  scopedSlots?: { [key: string]: ScopedSlot | undefined };
   ref?: string;
   refInFor?: boolean;
   tag?: string;