diff --git a/package.json b/package.json index 0353b1a29..78bccd06e 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "rollup-plugin-terser": "^2.0.2", "rollup-plugin-typescript2": "^0.17.0", "ts-jest": "^23.10.0", - "typescript": "^3.1.3", + "typescript": "^3.5.0", "yorkie": "^2.0.0" } } diff --git a/packages/observer/src/effect.ts b/packages/observer/src/effect.ts index 819d7e6a3..ea136aed5 100644 --- a/packages/observer/src/effect.ts +++ b/packages/observer/src/effect.ts @@ -120,8 +120,8 @@ export function trigger( // never been tracked return } - const effects = new Set() - const computedRunners = new Set() + const effects: Set = new Set() + const computedRunners: Set = new Set() if (type === OperationTypes.CLEAR) { // collection being cleared, trigger all effects for target depsMap.forEach(dep => { diff --git a/packages/runtime-core/__tests__/createComponent.spec.ts b/packages/runtime-core/__tests__/createComponent.spec.ts new file mode 100644 index 000000000..9821a2850 --- /dev/null +++ b/packages/runtime-core/__tests__/createComponent.spec.ts @@ -0,0 +1,36 @@ +import { createComponent } from '../src/component' +import { value } from '@vue/observer' + +test('createComponent type inference', () => { + const MyComponent = createComponent({ + props: { + a: Number, + b: { + type: String + } + }, + setup(props) { + props.a * 2 + props.b.slice() + return { + c: value(1), + d: { + e: value('hi') + } + } + }, + render({ state, props }) { + state.c * 2 + state.d.e.slice() + props.a * 2 + props.b.slice() + this.a * 2 + this.b.slice() + this.c * 2 + this.d.e.slice() + } + }) + MyComponent // avoid unused + // rename this file to .tsx to test TSX props inference + // ;() +}) diff --git a/packages/runtime-core/__tests__/inheritance.spec.ts b/packages/runtime-core/__tests__/inheritance.spec.ts deleted file mode 100644 index 61ca8058f..000000000 --- a/packages/runtime-core/__tests__/inheritance.spec.ts +++ /dev/null @@ -1,291 +0,0 @@ -import { - Component, - observable, - h, - nextTick, - KeepAlive, - ComponentPropsOptions, - ComponentWatchOptions -} from '@vue/runtime-core' -import { createInstance, renderInstance } from '@vue/runtime-test' - -describe('class inheritance', () => { - it('should merge data', () => { - class Base extends Component { - foo = 1 - data() { - return { - bar: 2 - } - } - } - - class Child extends Base { - foo: number - bar: number - baz: number - qux: number = 4 - - data(): any { - return { - baz: 3 - } - } - } - - const child = createInstance(Child) - - expect(child.foo).toBe(1) - expect(child.bar).toBe(2) - expect(child.baz).toBe(3) - expect(child.qux).toBe(4) - }) - - it('should merge props', () => { - class Base extends Component { - static props: ComponentPropsOptions = { - foo: Number - } - } - - class Child extends Base { - foo: number - bar: number - $props: { foo: number; bar: number } - static props: ComponentPropsOptions = { - bar: Number - } - } - - const child = createInstance(Child, { - foo: 1, - bar: 2 - }) - - expect(child.foo).toBe(1) - expect(child.bar).toBe(2) - expect(child.$props.foo).toBe(1) - expect(child.$props.bar).toBe(2) - }) - - it('should merge lifecycle hooks', async () => { - const calls: string[] = [] - const state = observable({ ok: true }) - - class Base extends Component { - beforeCreate() { - calls.push('base beforeCreate') - } - created() { - calls.push('base created') - } - beforeMount() { - calls.push('base beforeMount') - } - mounted() { - calls.push('base mounted') - } - beforeUpdate() { - calls.push('base beforeUpdate') - } - updated() { - calls.push('base updated') - } - beforeUnmount() { - calls.push('base beforeUnmount') - } - unmounted() { - calls.push('base unmounted') - } - } - - class Child extends Base { - beforeCreate() { - calls.push('child beforeCreate') - } - created() { - calls.push('child created') - } - beforeMount() { - calls.push('child beforeMount') - } - mounted() { - calls.push('child mounted') - } - beforeUpdate() { - calls.push('child beforeUpdate') - } - updated() { - calls.push('child updated') - } - beforeUnmount() { - calls.push('child beforeUnmount') - } - unmounted() { - calls.push('child unmounted') - } - render() { - return state.ok ? 'foo' : 'bar' - } - } - - class Container extends Component { - show = true - render() { - return this.show ? h(Child) : null - } - } - - const container = await renderInstance(Container) - expect(calls).toEqual([ - 'base beforeCreate', - 'child beforeCreate', - 'base created', - 'child created', - 'base beforeMount', - 'child beforeMount', - 'base mounted', - 'child mounted' - ]) - - calls.length = 0 - state.ok = false - await nextTick() - expect(calls).toEqual([ - 'base beforeUpdate', - 'child beforeUpdate', - 'base updated', - 'child updated' - ]) - - calls.length = 0 - container.show = false - await nextTick() - expect(calls).toEqual([ - 'base beforeUnmount', - 'child beforeUnmount', - 'base unmounted', - 'child unmounted' - ]) - }) - - it('should merge lifecycle hooks (activated/deactivated)', async () => { - const calls: string[] = [] - - class Base extends Component { - activated() { - calls.push('base activated') - } - deactivated() { - calls.push('base deactivated') - } - } - - class Child extends Base { - activated() { - calls.push('child activated') - } - deactivated() { - calls.push('child deactivated') - } - render() { - return 'foo' - } - } - - class Container extends Component { - ok = true - render() { - return h(KeepAlive, this.ok ? h(Child) : null) - } - } - - const container = await renderInstance(Container) - expect(container.$el.text).toBe('foo') - - container.ok = false - await nextTick() - expect(container.$el.text).toBe('') - expect(calls).toEqual(['base deactivated', 'child deactivated']) - - calls.length = 0 - container.ok = true - await nextTick() - expect(container.$el.text).toBe('foo') - expect(calls).toEqual(['base activated', 'child activated']) - }) - - it('should merge watchers', async () => { - const fooCallback = jest.fn() - const barCallback = jest.fn() - - class Base extends Component { - static watch: ComponentWatchOptions = { - foo: fooCallback - } - } - - class Child extends Base { - foo = 1 - bar = 2 - static watch: ComponentWatchOptions = { - bar: barCallback - } - } - - const child = createInstance(Child) - child.foo = 2 - await nextTick() - expect(fooCallback).toHaveBeenCalledWith(2, 1) - child.bar = 3 - await nextTick() - expect(barCallback).toHaveBeenCalledWith(3, 2) - }) - - it('should inherit methods', () => { - const fooCallback = jest.fn() - const barCallback = jest.fn() - - class Base extends Component { - foo() { - fooCallback() - } - } - - class Child extends Base { - bar() { - barCallback() - } - } - - const child = createInstance(Child) - child.foo() - child.bar() - expect(fooCallback).toHaveBeenCalled() - expect(barCallback).toHaveBeenCalled() - }) - - it('should inherit computed properties', () => { - class Base extends Component { - a = 1 - get foo() { - return this.a + 1 - } - } - - class Child extends Base { - b = 1 - get bar() { - return this.b + this.foo + 1 - } - } - - const child = createInstance(Child) - expect(child.foo).toBe(2) - expect(child.bar).toBe(4) - - child.a = 2 - expect(child.foo).toBe(3) - expect(child.bar).toBe(5) - }) -}) diff --git a/packages/runtime-core/__tests__/memoize.spec.ts b/packages/runtime-core/__tests__/memoize.spec.ts deleted file mode 100644 index bc6d5cbd9..000000000 --- a/packages/runtime-core/__tests__/memoize.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { h, Component, memoize, nextTick } from '../src' -import { renderInstance, serialize } from '@vue/runtime-test' - -describe('memoize', () => { - it('should work', async () => { - class App extends Component { - count = 1 - render() { - return h('div', [ - this.count, - this.count % 2 - ? memoize(() => h('div', `A` + this.count), this, 0) - : null, - memoize(() => h('div', `B` + this.count), this, 1) - ]) - } - } - - const app = await renderInstance(App) - expect(serialize(app.$el)).toBe(`
1
A1
B1
`) - - app.count++ - await nextTick() - expect(serialize(app.$el)).toBe(`
2
B1
`) - - app.count++ - await nextTick() - // test remounting a memoized tree - expect(serialize(app.$el)).toBe(`
3
A1
B1
`) - }) - - it('should invalidate based on keys', async () => { - class App extends Component { - foo = 1 - bar = 1 - render() { - return memoize(() => h('div', this.foo + this.bar), this, 0, [this.bar]) - } - } - - const app = await renderInstance(App) - expect(serialize(app.$el)).toBe(`
2
`) - - app.foo++ - await nextTick() - // should not update - expect(serialize(app.$el)).toBe(`
2
`) - - app.bar++ - await nextTick() - // should update now - expect(serialize(app.$el)).toBe(`
4
`) - }) -}) diff --git a/packages/runtime-core/__tests__/mixins.spec.ts b/packages/runtime-core/__tests__/mixins.spec.ts deleted file mode 100644 index 0c0d48246..000000000 --- a/packages/runtime-core/__tests__/mixins.spec.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { Component, ComponentClass, mixins } from '@vue/runtime-core' -import { createInstance } from '@vue/runtime-test' -import { prop } from '@vue/decorators' - -const calls: string[] = [] - -beforeEach(() => { - calls.length = 0 -}) - -class ClassMixinA extends Component<{ p1: string }, { d11: number }> { - // props - @prop - p1: string - // data - d1 = 1 - data() { - return { - d11: 2 - } - } - - // computed - get c1() { - return this.d1 + this.$data.d11 - } - - // lifecycle - created() { - calls.push('created from mixin A') - } - - // methods - foo() { - return this.d1 - } -} - -class ClassMixinB extends Component<{ p2: string }, { d21: number }> { - // props - static props = { - p2: String - } - - // data - d2 = 1 - data() { - return { - d21: 2 - } - } - - get c2() { - return this.d2 + this.$data.d21 - } - - // lifecycle - created() { - calls.push('created from mixin B') - } - - // methods - bar() { - return this.d2 - } -} - -const ObjectMixinA = { - props: { - p1: String - }, - data() { - return { - d1: 1, - d11: 2 - } - }, - computed: { - c1() { - return this.d1 + this.d11 - } - }, - created() { - calls.push('created from mixin A') - }, - methods: { - foo() { - return this.d1 - } - } -} - -const ObjectMixinB = { - props: { - p2: String - }, - data() { - return { - d2: 1, - d21: 2 - } - }, - computed: { - c2() { - return this.d2 + this.d21 - } - }, - created() { - calls.push('created from mixin B') - }, - methods: { - bar() { - return this.d2 - } - } -} - -function assertMixins(Test: any) { - const instance = createInstance(Test, { - p1: '1', - p2: '2', - p3: '3' - }) as any - - // data - expect(instance.d1).toBe(1) - expect(instance.d11).toBe(2) - expect(instance.d2).toBe(1) - expect(instance.d21).toBe(2) - expect(instance.d3).toBe(1) - expect(instance.d31).toBe(2) - - // props - expect(instance.p1).toBe('1') - expect(instance.p2).toBe('2') - expect(instance.p3).toBe('3') - expect(instance.$props.p1).toBe('1') - expect(instance.$props.p2).toBe('2') - expect(instance.$props.p3).toBe('3') - - // computed - expect(instance.c1).toBe(3) - expect(instance.c2).toBe(3) - expect(instance.c3).toBe(3) - - // lifecycle - expect(calls).toEqual([ - 'created from mixin A', - 'created from mixin B', - 'created from Test' - ]) - - // methods - expect(instance.foo()).toBe(1) - expect(instance.bar()).toBe(1) - expect(instance.baz()).toBe(1) -} - -describe('mixins', () => { - it('should work with classes', () => { - class Test extends mixins(ClassMixinA, ClassMixinB)< - { p3: string }, - { d31: number } - > { - static props = { - p3: String - } - - d3 = 1 - data(): any { - return { - d31: 2 - } - } - - get c3() { - return this.d3 + this.d31 - } - - created() { - calls.push('created from Test') - } - - baz() { - return this.d3 - } - } - - const instance = createInstance(Test, { - p1: '1', - p2: '2', - p3: '3' - }) - - // we duplicate the assertions because they serve as type tests as well - - // data - expect(instance.d1).toBe(1) - expect(instance.$data.d11).toBe(2) - expect(instance.d2).toBe(1) - expect(instance.$data.d21).toBe(2) - expect(instance.d3).toBe(1) - expect(instance.d31).toBe(2) - - // props - expect(instance.p1).toBe('1') - expect(instance.$props.p2).toBe('2') - expect(instance.p3).toBe('3') - expect(instance.$props.p1).toBe('1') - expect(instance.$props.p2).toBe('2') - expect(instance.$props.p3).toBe('3') - - // computed - expect(instance.c1).toBe(3) - expect(instance.c2).toBe(3) - expect(instance.c3).toBe(3) - - // lifecycle - expect(calls).toEqual([ - 'created from mixin A', - 'created from mixin B', - 'created from Test' - ]) - - // methods - expect(instance.foo()).toBe(1) - expect(instance.bar()).toBe(1) - expect(instance.baz()).toBe(1) - }) - - it('should work with objects', () => { - class Test extends ((mixins as any)( - ObjectMixinA, - ObjectMixinB - ) as ComponentClass)<{ p3: string }, { d31: number }> { - static props = { - p3: String - } - - d3 = 1 - data(): any { - return { - d31: 2 - } - } - - get c3() { - return this.d3 + this.$data.d31 - } - - created() { - calls.push('created from Test') - } - - baz() { - return this.d3 - } - } - - assertMixins(Test) - }) - - it('should work with a mix of objects and classes', () => { - class Test extends ((mixins as any)( - ClassMixinA, - ObjectMixinB - ) as ComponentClass)<{ p3: string }, { d31: number }> { - static props = { - p3: String - } - - d3 = 1 - data(): any { - return { - d31: 2 - } - } - - get c3() { - return this.d3 + this.$data.d31 - } - - created() { - calls.push('created from Test') - } - - baz() { - return this.d3 - } - } - - assertMixins(Test) - }) -}) diff --git a/packages/runtime-core/__tests__/parentChain.spec.ts b/packages/runtime-core/__tests__/parentChain.spec.ts deleted file mode 100644 index c010c9dfc..000000000 --- a/packages/runtime-core/__tests__/parentChain.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { - h, - Component, - observable, - nextTick, - renderInstance -} from '@vue/runtime-test' - -describe('Parent chain management', () => { - it('should have correct $parent / $root / $children', async () => { - let child: any - let grandChildren: any[] = [] - - const state = observable({ ok: true }) - - class Parent extends Component { - render() { - return h(Child) - } - } - - class Child extends Component { - created() { - child = this - } - render() { - return [state.ok ? h(GrandChild) : null, h(GrandChild)] - } - } - - class GrandChild extends Component { - created() { - grandChildren.push(this) - } - unmounted() { - grandChildren.splice(grandChildren.indexOf(this), 1) - } - render() { - return h('div') - } - } - - const parent = await renderInstance(Parent) - - expect(child.$parent).toBe(parent) - expect(child.$root).toBe(parent) - - grandChildren.forEach(grandChild => { - expect(grandChild.$parent).toBe(child) - expect(grandChild.$root).toBe(parent) - }) - - expect(parent.$children).toEqual([child]) - expect(grandChildren.length).toBe(2) - expect(child.$children).toEqual(grandChildren) - - state.ok = false - await nextTick() - expect(grandChildren.length).toBe(1) - expect(child.$children).toEqual(grandChildren) - }) - - it('should have correct $parent / $root w/ functional component in between', async () => { - let child: any - let grandChildren: any[] = [] - - const state = observable({ ok: true }) - - class Parent extends Component { - render() { - return h(FunctionalChild) - } - } - - const FunctionalChild = () => h(Child) - - class Child extends Component { - created() { - child = this - } - render() { - return [ - state.ok ? h(FunctionalGrandChild) : null, - h(FunctionalGrandChild) - ] - } - } - - const FunctionalGrandChild = () => h(GrandChild) - - class GrandChild extends Component { - created() { - grandChildren.push(this) - } - unmounted() { - grandChildren.splice(grandChildren.indexOf(this), 1) - } - render() {} - } - - const parent = await renderInstance(Parent) - - expect(child.$parent).toBe(parent) - expect(child.$root).toBe(parent) - - grandChildren.forEach(grandChild => { - expect(grandChild.$parent).toBe(child) - expect(grandChild.$root).toBe(parent) - }) - - expect(parent.$children).toEqual([child]) - expect(grandChildren.length).toBe(2) - expect(child.$children).toEqual(grandChildren) - - state.ok = false - await nextTick() - expect(grandChildren.length).toBe(1) - expect(child.$children).toEqual(grandChildren) - }) -}) diff --git a/packages/runtime-core/jsx.d.ts b/packages/runtime-core/jsx.d.ts index a7110a110..6756af384 100644 --- a/packages/runtime-core/jsx.d.ts +++ b/packages/runtime-core/jsx.d.ts @@ -1,7 +1,7 @@ declare namespace JSX { interface Element {} interface ElementClass { - render(props: any, slots: any, attrs: any): any + $props: {} } interface ElementAttributesProperty { $props: {} diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index 7e64d48d8..81356f1ce 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -20,7 +20,6 @@ }, "homepage": "https://github.com/vuejs/vue/tree/dev/packages/runtime-core#readme", "dependencies": { - "@vue/observer": "3.0.0-alpha.1", - "@vue/scheduler": "3.0.0-alpha.1" + "@vue/observer": "3.0.0-alpha.1" } } diff --git a/packages/runtime-dom/jsx.d.ts b/packages/runtime-dom/jsx.d.ts index c66a0166d..059da503c 100644 --- a/packages/runtime-dom/jsx.d.ts +++ b/packages/runtime-dom/jsx.d.ts @@ -751,7 +751,7 @@ type NativeElements = { declare namespace JSX { interface Element {} interface ElementClass { - render(props: any, slots: any, attrs: any): any + $props: {} } interface ElementAttributesProperty { $props: {} diff --git a/yarn.lock b/yarn.lock index 6e0a4d6f1..e2edd9616 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6072,10 +6072,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.3.tgz#01b70247a6d3c2467f70c45795ef5ea18ce191d5" - integrity sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA== +typescript@^3.5.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202" + integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw== uglify-js@^3.1.4: version "3.4.9"