Skip to content

Commit

Permalink
feat(types): mixins/extends support in TypeScript (#626)
Browse files Browse the repository at this point in the history
  • Loading branch information
dolymood authored Jun 9, 2020
1 parent 97dedeb commit d3c436a
Show file tree
Hide file tree
Showing 11 changed files with 656 additions and 56 deletions.
6 changes: 3 additions & 3 deletions packages/reactivity/__tests__/collections/Set.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,13 +412,13 @@ describe('reactivity/collections', () => {
`Reactive Set contains both the raw and reactive`
).toHaveBeenWarned()
})

it('thisArg', () => {
const raw = new Set([ 'value' ])
const raw = new Set(['value'])
const proxy = reactive(raw)
const thisArg = {}
let count = 0
proxy.forEach(function (this :{}, value, _, set) {
proxy.forEach(function(this: {}, value, _, set) {
++count
expect(this).toBe(thisArg)
expect(value).toBe('value')
Expand Down
2 changes: 1 addition & 1 deletion packages/reactivity/src/baseHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function createGetter(isReadonly = false, shallow = false) {
}
const res = Reflect.get(target, key, receiver)

if (isSymbol(key) && builtInSymbols.has(key) || key === '__proto__') {
if ((isSymbol(key) && builtInSymbols.has(key)) || key === '__proto__') {
return res
}

Expand Down
108 changes: 97 additions & 11 deletions packages/runtime-core/__tests__/apiOptions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,11 @@ describe('api: options', () => {
}
}
const mixinB = {
props: {
bP: {
type: String
}
},
data() {
return {
b: 2
Expand All @@ -452,40 +457,65 @@ describe('api: options', () => {
calls.push('mixinB created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
expect(this.bP).toBeUndefined()
expect(this.c).toBe(3)
expect(this.cP1).toBeUndefined()
},
mounted() {
calls.push('mixinB mounted')
}
}
const Comp = {
mixins: [mixinA, mixinB],
const mixinC = defineComponent({
props: ['cP1', 'cP2'],
data() {
return {
c: 3
}
},
created(this: any) {
created() {
calls.push('mixinC created')
expect(this.c).toBe(3)
expect(this.cP1).toBeUndefined()
},
mounted() {
calls.push('mixinC mounted')
}
})
const Comp = defineComponent({
props: {
aaa: String
},
mixins: [defineComponent(mixinA), defineComponent(mixinB), mixinC],
data() {
return {
z: 4
}
},
created() {
calls.push('comp created')
expect(this.a).toBe(1)
expect(this.b).toBe(2)
expect(this.bP).toBeUndefined()
expect(this.c).toBe(3)
expect(this.cP2).toBeUndefined()
expect(this.z).toBe(4)
},
mounted() {
calls.push('comp mounted')
},
render(this: any) {
render() {
return `${this.a}${this.b}${this.c}`
}
}

})
expect(renderToString(h(Comp))).toBe(`123`)
expect(calls).toEqual([
'mixinA created',
'mixinB created',
'mixinC created',
'comp created',
'mixinA mounted',
'mixinB mounted',
'mixinC mounted',
'comp mounted'
])
})
Expand All @@ -498,12 +528,17 @@ describe('api: options', () => {
a: 1
}
},
mounted() {
methods: {
sayA() {}
},
mounted(this: any) {
expect(this.a).toBe(1)
expect(this.b).toBe(2)
calls.push('base')
}
}
const Comp = {
extends: Base,
const Comp = defineComponent({
extends: defineComponent(Base),
data() {
return {
b: 2
Expand All @@ -512,15 +547,66 @@ describe('api: options', () => {
mounted() {
calls.push('comp')
},
render(this: any) {
render() {
return `${this.a}${this.b}`
}
}
})

expect(renderToString(h(Comp))).toBe(`12`)
expect(calls).toEqual(['base', 'comp'])
})

test('extends with mixins', () => {
const calls: string[] = []
const Base = {
data() {
return {
a: 1
}
},
methods: {
sayA() {}
},
mounted(this: any) {
expect(this.a).toBe(1)
expect(this.b).toBeTruthy()
expect(this.c).toBe(2)
calls.push('base')
}
}
const Base2 = {
data() {
return {
b: true
}
},
mounted(this: any) {
expect(this.a).toBe(1)
expect(this.b).toBeTruthy()
expect(this.c).toBe(2)
calls.push('base2')
}
}
const Comp = defineComponent({
extends: defineComponent(Base),
mixins: [defineComponent(Base2)],
data() {
return {
c: 2
}
},
mounted() {
calls.push('comp')
},
render() {
return `${this.a}${this.b}${this.c}`
}
})

expect(renderToString(h(Comp))).toBe(`1true2`)
expect(calls).toEqual(['base', 'base2', 'comp'])
})

test('accessing setup() state from options', async () => {
const Comp = defineComponent({
setup() {
Expand Down
105 changes: 90 additions & 15 deletions packages/runtime-core/src/apiDefineComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import {
ComponentOptionsWithoutProps,
ComponentOptionsWithArrayProps,
ComponentOptionsWithObjectProps,
ComponentOptionsMixin,
RenderFunction
} from './componentOptions'
import { SetupContext, FunctionalComponent } from './component'
import { ComponentPublicInstance } from './componentProxy'
import {
CreateComponentPublicInstance,
ComponentPublicInstanceConstructor
} from './componentProxy'
import { ExtractPropTypes, ComponentPropsOptions } from './componentProps'
import { EmitsOptions } from './componentEmits'
import { isFunction } from '@vue/shared'
Expand All @@ -25,17 +29,21 @@ export function defineComponent<Props, RawBindings = object>(
props: Readonly<Props>,
ctx: SetupContext
) => RawBindings | RenderFunction
): {
new (): ComponentPublicInstance<
): ComponentPublicInstanceConstructor<
CreateComponentPublicInstance<
Props,
RawBindings,
{},
{},
{},
{},
{},
{},
// public props
VNodeProps & Props
>
} & FunctionalComponent<Props>
> &
FunctionalComponent<Props>

// overload 2: object format with no props
// (uses user defined props interface)
Expand All @@ -46,21 +54,46 @@ export function defineComponent<
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
options: ComponentOptionsWithoutProps<Props, RawBindings, D, C, M, E, EE>
): {
new (): ComponentPublicInstance<
options: ComponentOptionsWithoutProps<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE
>
): ComponentPublicInstanceConstructor<
CreateComponentPublicInstance<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
VNodeProps & Props
>
} & ComponentOptionsWithoutProps<Props, RawBindings, D, C, M, E, EE>
> &
ComponentOptionsWithoutProps<
Props,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE
>

// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: any }
Expand All @@ -71,6 +104,8 @@ export function defineComponent<
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
Expand All @@ -80,13 +115,36 @@ export function defineComponent<
D,
C,
M,
Mixin,
Extends,
E,
EE
>
): ComponentPublicInstanceConstructor<
// array props technically doesn't place any contraints on props in TSX before,
// but now we can export array props in TSX
CreateComponentPublicInstance<
Readonly<{ [key in PropNames]?: any }>,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E
>
> &
ComponentOptionsWithArrayProps<
PropNames,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE
>
): {
// array props technically doesn't place any constraints on props in TSX
new (): ComponentPublicInstance<VNodeProps, RawBindings, D, C, M, E>
} & ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M, E, EE>

// overload 4: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
Expand All @@ -98,6 +156,8 @@ export function defineComponent<
D,
C extends ComputedOptions = {},
M extends MethodOptions = {},
Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
E extends EmitsOptions = Record<string, any>,
EE extends string = string
>(
Expand All @@ -107,20 +167,35 @@ export function defineComponent<
D,
C,
M,
Mixin,
Extends,
E,
EE
>
): {
new (): ComponentPublicInstance<
): ComponentPublicInstanceConstructor<
CreateComponentPublicInstance<
ExtractPropTypes<PropsOptions>,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
VNodeProps & ExtractPropTypes<PropsOptions, false>
>
} & ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M, E, EE>
> &
ComponentOptionsWithObjectProps<
PropsOptions,
RawBindings,
D,
C,
M,
Mixin,
Extends,
E,
EE
>

// implementation, close to no-op
export function defineComponent(options: unknown) {
Expand Down
Loading

0 comments on commit d3c436a

Please sign in to comment.