Как можно использовать базовый класс и миксины с компонентом vue-class-component?

Как я могу использовать как базовый класс, так и миксин с vue-class-component?

Связывание функций из миксина непосредственно в шаблоне работает просто отлично, но попытка использовать эти функции в коде машинописного текста приведет к ошибке при компиляции.

Из того, что я понимаю, чтобы использовать миксины, вы должны исходить из них:

class MyComp extends mixins(MixinA, MixinB)

К сожалению, у меня уже есть базовый класс, так что это не вариант. Вот код, который у меня есть... Есть ли способ сделать это с помощью vue-class-component?

// mixin
@Component
export default class DownloadService extends Vue {
  public downloadModel(id: string) {
    alert('Downloading.');
  }
}

// Base class
@Component({
  props: {
    id: String
  }
})
export default class BaseCard extends Vue {
  id: string;

  protected delete() {
    alert('Deleted');
  }
}


// Child class
@Component({
  props: {
    id: String,
    disabled: Boolean,
  },
  mixins: [DownloadService],
})
export default class ItemCard extends BaseCard {
  protected clicked() {
    // Causes error = S2339: Property 'downloadModel' does not exist on type 'ItemCard'.
    this.downloadModel(this.id);
  }
}

Обратите внимание, что я могу привести "this" к необходимости, что сработает, но это кажется проблематичным, если мне придется делать это повсеместно:

  protected clicked() {
    // Ideally shouldn't have to do this casting everywhere. 
    (<DownloadService><any>this).downloadModel(this.id);
  }

1 ответ

Решение

Вы действительно не можете расширить несколько классов. Однако TypeScript также поддерживает миксины.

Сначала вам нужно реализовать класс mixin:

// Child class
@Component({
  props: {
    id: String,
    disabled: Boolean,
  },
  mixins: [DownloadService],
})
export default class ItemCard extends BaseCard implements DownloadService {
  protected clicked() {
    this.downloadModel(this.id);
  }

  // DownloadService declaration
  public downloadModel: (id: string) => void;
}

А затем вызовите функцию, которая применяет миксин к вашему классу:

applyMixins(ItemCard, [DownloadService]);

Функция applyMixins это функция, которую вы должны включить где-нибудь во время выполнения:

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

См. Документацию TypeScript по миксинам для получения дополнительной информации: https://www.typescriptlang.org/docs/handbook/mixins.html


Другой вариант - использовать шаблон класса миксина ECMAScript 2015, который также поддерживается начиная с TypeScript 2.2.

Сначала вы должны определить тип где-то для сигнатуры конструкции для определенного типа:

type Constructor<T> = new(...args: any[]) => T;

Затем создайте функцию, которая возвращает новый класс с вашей функцией mixin:

function Downloadable<T extends Constructor<{}>>(Base: T) {
  return class extends Base {
    public downloadModel(id: string) {
      alert('Downloading.');
    }
  }
}

Тогда вам все еще нужен класс с @Component декоратор, чтобы определить миксин для Vue:

// mixin
@Component
export class DownloadService extends Downloadable(Vue) {
}

Наконец, вы можете определить класс, в котором вы применяете миксин к базовому классу:

// Child class
@Component({
  props: {
    id: String,
    disabled: Boolean,
  },
  mixins: [DownloadService],
})
export default class ItemCard extends Downloadable(BaseCard) {
  protected clicked() {
    this.downloadModel(this.id);
  }
}
Другие вопросы по тегам