Repository: vuejs/composition-api (ts)

Issue:
`Vue.set` does not work with reactive objects (regression in beta 15)
`Vue.set` is now failing on reactive objects (but only on SSR):

```js
const a = reactive({})
Vue.set(a, 'prop', 'value')
```

See https://codesandbox.io/s/wizardly-glitter-v9nd0?file=/pages/index.vue for a reproduction.

https://github.com/nuxt-community/composition-api/pull/251

Pull request:
fix(SSR): value set for props, fix #550
fix #550

Patch:
diff --git a/src/component/componentOptions.ts b/src/component/componentOptions.ts
index a6cc5d2..4774314 100644
--- a/src/component/componentOptions.ts
+++ b/src/component/componentOptions.ts
@@ -70,6 +70,7 @@ export type ComponentOptionsWithProps<
   Props = ExtractPropTypes<PropsOptions>
 > = ComponentOptionsBase<Props, D, C, M> & {
   props?: PropsOptions
+  emits?: string[] | Record<string, null | ((emitData: any) => boolean) >
   setup?: SetupFunction<Props, RawBindings>
 } & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
 
@@ -82,6 +83,7 @@ export type ComponentOptionsWithArrayProps<
   Props = Readonly<{ [key in PropNames]?: any }>
 > = ComponentOptionsBase<Props, D, C, M> & {
   props?: PropNames[]
+  emits?: string[] | Record<string, null | ((emitData: any) => boolean)>
   setup?: SetupFunction<Props, RawBindings>
 } & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
 
@@ -93,6 +95,7 @@ export type ComponentOptionsWithoutProps<
   M extends MethodOptions = {}
 > = ComponentOptionsBase<Props, D, C, M> & {
   props?: undefined
+  emits?: string[] | Record<string, null | ((emitData: any) => boolean)>
   setup?: SetupFunction<Props, RawBindings>
 } & ThisType<ComponentRenderProxy<Props, RawBindings, D, C, M>>
 
diff --git a/src/reactivity/reactive.ts b/src/reactivity/reactive.ts
index 1006455..e594e53 100644
--- a/src/reactivity/reactive.ts
+++ b/src/reactivity/reactive.ts
@@ -1,6 +1,6 @@
 import { AnyObject } from '../types/basic'
 import { getRegisteredVueOrDefault } from '../runtimeContext'
-import { isPlainObject, def, warn, isArray, hasOwn } from '../utils'
+import { isPlainObject, def, warn, isArray, hasOwn, noopFn } from '../utils'
 import { isComponentInstance, defineComponentInstance } from '../utils/helper'
 import { RefKey } from '../utils/symbols'
 import { isRef, UnwrapRef } from './ref'
@@ -113,14 +113,26 @@ function observe<T>(obj: T): T {
 
   // in SSR, there is no __ob__. Mock for reactivity check
   if (!hasOwn(observed, '__ob__')) {
-    def(observed, '__ob__', {})
+    def(observed, '__ob__', mockObserver(observed))
   }
 
   return observed
 }
 
 export function createObserver() {
-  return observe<any>({}).__ob__ || {}
+  return observe<any>({}).__ob__
+}
+
+function mockObserver(value: any = {}): any {
+  return {
+    value,
+    dep: {
+      notify: noopFn,
+      depend: noopFn,
+      addSub: noopFn,
+      removeSub: noopFn,
+    },
+  }
 }
 
 export function shallowReactive<T extends object = any>(obj: T): T


Test patch:
diff --git a/test/ssr/ssrReactive.spec.ts b/test/ssr/ssrReactive.spec.ts
index befa01c..5c8510c 100644
--- a/test/ssr/ssrReactive.spec.ts
+++ b/test/ssr/ssrReactive.spec.ts
@@ -3,7 +3,15 @@
  */
 
 import Vue from '../vue'
-import { isReactive, reactive, ref, isRaw, isRef, shallowRef } from '../../src'
+import {
+  isReactive,
+  reactive,
+  ref,
+  isRaw,
+  isRef,
+  set,
+  shallowRef,
+} from '../../src'
 import { createRenderer } from 'vue-server-renderer'
 
 describe('SSR Reactive', () => {
@@ -44,4 +52,42 @@ describe('SSR Reactive', () => {
     expect(isRef(state)).toBe(true)
     expect(isRaw(state)).toBe(false)
   })
+
+  // #550
+  it('props should work with set', async (done) => {
+    let props: any
+
+    const app = new Vue({
+      render(this: any, h) {
+        return h('child', { attrs: { msg: this.msg } })
+      },
+      setup() {
+        return { msg: ref('hello') }
+      },
+      components: {
+        child: {
+          render(this: any, h: any) {
+            return h('span', this.data.msg)
+          },
+          props: ['msg'],
+          setup(_props) {
+            props = _props
+
+            return { data: _props }
+          },
+        },
+      },
+    })
+
+    const serverRenderer = createRenderer()
+    const html = await serverRenderer.renderToString(app)
+
+    expect(html).toBe('<span data-server-rendered="true">hello</span>')
+
+    expect(props.bar).toBeUndefined()
+    set(props, 'bar', 'bar')
+    expect(props.bar).toBe('bar')
+
+    done()
+  })
 })