Using mixins with TypeScript

Hi, I’m having this issue when I’m trying to use a mixin in a component using TypeScript.

// MyMixin.ts
import Vue from "vue";

export default Vue.extend({
  methods: {
    myMixinMethod() {
      // ...
    }    
  }
});
<!-- MyComponent.vue -->

<template>...</template>

<script lang="ts">
import Vue from "vue";
import MyMixin from "./MyMixin";

export default Vue.extend({
  mixins: [MyMixin],

  methods: {
    this.myMixinMethod() // Error: Property 'myMixinMethod' does not exist on type 'CombinedVueInstance<Vue
  }
});
</script>

Please note in the above code the error when I try to use this.myMixinMethod(),TypeScript won’t recognize it as a property of this

I see that using vue-class-component it’s as easy as:

export default class MyComponent extends Vue implements MyMixin{}

But I don’t want to use vue-class-component. Any ideas how I can solve this issue?

Edit: Reading this thread again, it looks a bit tough to get a smooth UX with typescript and mixins.

1 Like

I’ve had the same problem - there’s a few code examples floating around in GitHub issues etc, but I decided to put them in a module for easier consumption: vue-mixin-decorator.

It does however, have a strong dependency on vue-class-component so may not solve your problem.

Thanks for your reply @justrhysism. Your plugin seems to solve the issue if one using vue-class-component. Nice job! Hope this issue is fixed in Vue at some point.

I’m currently working around the Vue mixin functionality by simply using a regular .ts file for common functionality:

export const commonMethods = {
  someMethod() { ... }
}

and then adding them manually in the main component(s):

import { commonMethods } from './my-fake-mixin-file.ts'

export default Vue.extend({
  ...
  methods: {
    someSpecificMethod() { ... },
    ...commonMethods,
  }
})

And so on for all other properties (props, computed, …). It’s a bit tedious and it requires me to manually type this in the mixin file, but it plugs into type inference in the main component quite well.

Has anyone found any more elegant solutions to this?

1 Like

I wouldn’t consider it more elegant, I’ve been working through the same issue with a less verbose solution. Currently, we define mixins in separate .ts files, like so:

import Vue from 'vue'
export default Vue.extend({ ...})

and then having the implementing component extend it, like so:

import MixinExample from './MixinExample'
export default MixinExample.extend({ ... })

Which will give you type-checking for mixin methods. With this setup, you lose the ability to define multiple mixins in a single component. It also goes against the VueJS mixin API. I would be interested in seeing a more ‘official’ solution for Typescript.

2 Likes

I was using the same approach as @mmitchellgarcia but it’s not ideal as he says. After some experimentations, I’ve decided not to use Vue with TypeScript for now. Not only this issue but many community built plugins don’t have TS support, SFCs and vetur are not smooth with TS and all these issues will slow down the development. I’ll probably revisit later.

Can not solve the Vue mixin approach with inheritance in typescript? Is not the same?

// parent.component.ts
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component({})
export default class ParentComponent extends Vue {
  @Prop() protected mySuperProp!: string;

  protected get parentComputedProperty() {
    return 'something';
  }
}

// child.component.ts
import { Component, Vue } from 'vue-property-decorator';
import ParentComponent from './parent.compoent';

@Component({})
export default class ChildComponent extends ParentComponent {
  private saySomething() {
    console.log(this.mySuperProp, this.parentComputedProperty); // this works
  }
}

Regards!

I ran into this issue needing to extend with two mixins, and came across this great package: https://github.com/ktsn/vue-typed-mixins I thought I’d share since this thread is the top result from Google.

I personally don’t like the class style decorators; the standard Vue syntax is a lot easier to parse. The library allows TypeScript to understand multiple mixins using standard Vue syntax. Works great! Example adapted from the readme:

const Foo = Vue.extend({
  data() {
    return {
      foo: 'test'
    }
  }
})

const Bar = Vue.extend({
  data() {
    return {
      bar: 123
    }
  }
})

// component.ts
import mixins from 'vue-typed-mixins'

export default mixins(Foo, Bar).extend({
  computed: {
    concat(): string {
      return `${this.foo} ${this.bar} ${this.value}`
    }
  }
})

You can composite mixins the same way.

5 Likes

@brainbag You totally save my day! Happy Christmas! :grinning:

What a pity, plugins just not working with this.

1 Like

Isn’t @Mixins available now?

It’s available if you choose to use vue-class-component, which doesn’t seem to be the case here.

Is it possible to implement a type that will take a dynamic number of arguments?
An implementation in vue-typed-mixins has a limited number of arguments.

I found a solution to the problem by combining the implementations of vue-typed-mixins and vuetify.

File mixins.ts:

import Vue, { VueConstructor } from 'vue';
export type VueExtendCtor<T> = VueConstructor<Vue & T>;
export default function mixins<A>(Ctor1: VueExtendCtor<A>): VueExtendCtor<A>;
export default function mixins<A, B>(
    Ctor1: VueExtendCtor<A>,
    Ctor2: VueExtendCtor<B>
): VueExtendCtor<A & B>;
export default function mixins<A, B, C>(
    Ctor1: VueExtendCtor<A>,
    Ctor2: VueExtendCtor<B>,
    Ctor3: VueExtendCtor<C>
): VueExtendCtor<A & B & C>;
// ... Desired count of overloads ...
export default function mixins<V extends VueConstructor[]>(...mixins: V): VueExtendCtor<ExtractVue<V>>;
export default function mixins<V extends VueConstructor[]>(...mixins: V): VueExtendCtor<ExtractVue<V>> {
    if (!mixins.length) {
        throw new Error("At least one argument must be passed to function!");
    }
    if (mixins.length === 1) {
        return Vue.extend(mixins[0]);
    }
    return mixins.shift()!.extend({ mixins });
}

/**
 * Borrowed from Vuetify {@link https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/util/mixins.ts}
 * Returns the instance type from a VueConstructor
 * Useful for adding types when using mixins().extend()
 */
export type ExtractVue<T extends VueConstructor|VueConstructor[]> = T extends (infer U)[]
    ? UnionToIntersection<U extends VueConstructor<infer V> ? V : never>
    : T extends VueConstructor<infer V> ? V : never;
export type UnionToIntersection<U> =
    (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

Why is this a solution? As said in the README of vue-mixin-decorator:

Citaat
Note: @Mixin is @Component exported from vue-class-component .

You are just writing a Vue component and inherit from it. There’s no need to introduce a new library for this, as this can be accomplished with Vue out-of-the-box. Or am I missing something?

It’s been two years since this comment above about tool support. I don’t use many community plugins, but I found WebStorm’s support for TS + SFCs with the Mixin.extend({ ... }) approach works great. Obviously it only solves it for a single mix-in, but it works fine for my current use case.

Are you aware of this?

https://class-component.vuejs.org/guide/extend-and-mixins.html#mixins

Not sure if this solves your issue, but it didn’t exist two years ago, I think.

Another possible solution is to declare the mixin method or member in the shim file. My shim file is shims-vue.d.ts

Something like this:

declare module "vue/types/vue" {
	interface Vue {
		myMixinMethod(): null;
		myMixinValue: string;
	}
}