diff --git a/README.md b/README.md index 11976a3..68d48c7 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,40 @@ export default class App extends Vue { You may also want to check out the `@prop` and `@watch` decorators provided by [vue-property-decorators](https://github.com/kaorun343/vue-property-decorator). +### Using Mixins + +vue-class-component provides `mixins` helper function to use [mixins](https://vuejs.org/v2/guide/mixins.html) in class style manner. By using `mixins` helper, TypeScript can infer mixin types and inherit them on the component type. + +Example of declaring a mixin: + +``` js +// mixin.js +import Vue from 'vue' +import Component from 'vue-class-component' + +// You can declare a mixin as the same style as components. +@Component +export class MyMixin extends Vue { + mixinValue = 'Hello' +} +``` + +Example of using a mixin: + +``` js +import Component, { mixins } from 'vue-class-component' +import MyMixin from './mixin.js' + +// Use `mixins` helper function instead of `Vue`. +// `mixins` can receive any number of arguments. +@Component +export class MyComp extends mixins(MyMixin) { + created () { + console.log(this.mixinValue) // -> Hello + } +} +``` + ### Create Custom Decorators You can extend the functionality of this library by creating your own decorators. vue-class-component provides `createDecorator` helper to create custom decorators. `createDecorator` expects a callback function as the 1st argument and the callback will receive following arguments: diff --git a/src/declarations.ts b/src/declarations.ts index e1c349f..af7981f 100644 --- a/src/declarations.ts +++ b/src/declarations.ts @@ -1,6 +1,6 @@ import Vue, { ComponentOptions } from 'vue' -export type VueClass = { new (...args: any[]): V } & typeof Vue +export type VueClass = { new (...args: any[]): V & Vue } & typeof Vue export type DecoratedClass = VueClass & { // Property, method and parameter decorators created by `createDecorator` helper diff --git a/src/index.ts b/src/index.ts index 16b230a..f9e6f09 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,7 @@ import Vue, { ComponentOptions } from 'vue' import { VueClass } from './declarations' import { componentFactory, $internalHooks } from './component' -export { createDecorator, VueDecorator } from './util' +export { createDecorator, VueDecorator, mixins } from './util' function Component (options: ComponentOptions & ThisType): >(target: VC) => VC function Component >(target: VC): VC diff --git a/src/util.ts b/src/util.ts index 4793454..58f6f8e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ import Vue, { ComponentOptions } from 'vue' -import { DecoratedClass } from './declarations' +import { VueClass, DecoratedClass } from './declarations' export const noop = () => {} @@ -31,6 +31,16 @@ export function createDecorator (factory: (options: ComponentOptions, key: } } +export function mixins (CtorA: VueClass): VueClass +export function mixins (CtorA: VueClass, CtorB: VueClass): VueClass +export function mixins (CtorA: VueClass, CtorB: VueClass, CtorC: VueClass): VueClass +export function mixins (CtorA: VueClass, CtorB: VueClass, CtorC: VueClass, CtorD: VueClass): VueClass +export function mixins (CtorA: VueClass, CtorB: VueClass, CtorC: VueClass, CtorD: VueClass, CtorE: VueClass): VueClass +export function mixins (...Ctors: VueClass[]): VueClass +export function mixins (...Ctors: VueClass[]): VueClass { + return Vue.extend({ mixins: Ctors }) +} + export function isPrimitive (value: any): boolean { const type = typeof value return value == null || (type !== "object" && type !== "function") diff --git a/test/test-babel.js b/test/test-babel.js index ecbade1..f42cccb 100644 --- a/test/test-babel.js +++ b/test/test-babel.js @@ -1,4 +1,4 @@ -import Component, { createDecorator } from '../lib' +import Component, { createDecorator, mixins } from '../lib' import { expect } from 'chai' import * as td from 'testdouble' import Vue from 'vue' @@ -121,4 +121,31 @@ describe('vue-class-component with Babel', () => { expect(MyComp.foo).to.equal('foo') expect(MyComp.bar()).to.equal('bar') }) + + it('mixin helper', function () { + @Component + class MixinA extends Vue { + valueA = 'hello' + } + + @Component + class MixinB extends Vue { + valueB = 123 + } + + @Component + class MyComp extends mixins(MixinA, MixinB) { + test () { + this.valueA = 'hi' + this.valueB = 456 + } + } + + const vm = new MyComp() + expect(vm.valueA).to.equal('hello') + expect(vm.valueB).to.equal(123) + vm.test() + expect(vm.valueA).to.equal('hi') + expect(vm.valueB).to.equal(456) + }) }) diff --git a/test/test.ts b/test/test.ts index 5f818df..9ff686c 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,4 +1,4 @@ -import Component, { createDecorator } from '../lib' +import Component, { createDecorator, mixins } from '../lib' import { expect } from 'chai' import * as td from 'testdouble' import Vue, { ComputedOptions } from 'vue' @@ -342,4 +342,31 @@ describe('vue-class-component', () => { console.warn = originalWarn } }) + + it('mixin helper', function () { + @Component + class MixinA extends Vue { + valueA = 'hello' + } + + @Component + class MixinB extends Vue { + valueB = 123 + } + + @Component + class MyComp extends mixins(MixinA, MixinB) { + test () { + this.valueA = 'hi' + this.valueB = 456 + } + } + + const vm = new MyComp() + expect(vm.valueA).to.equal('hello') + expect(vm.valueB).to.equal(123) + vm.test() + expect(vm.valueA).to.equal('hi') + expect(vm.valueB).to.equal(456) + }) })