0

我所做
的我最初的方法是使用以下返回类装饰器的mixin:

function createMixin (behaviour: any, sharedBehaviour: any = {}): any {
  const instanceKeys: any = Reflect.ownKeys(behaviour)
  const sharedKeys: any = Reflect.ownKeys(sharedBehaviour)
  const typeTag = Symbol(`isa`)

  function _mixin (clazz: Function): Function {
    for (let property of instanceKeys) {
      Object.defineProperty(clazz.prototype, property, {
        value: behaviour[property],
        writable: true
      })
    }

    Object.defineProperty(clazz.prototype, typeTag, { value: true })

    return clazz
  }

  for (let property of sharedKeys) {
    Object.defineProperty(_mixin, property, {
      value: sharedBehaviour[property],
      enumerable: sharedBehaviour.propertyIsEnumerable(property)
    })
  }

  Object.defineProperty(_mixin, Symbol.hasInstance, {
    value: (i: any) => !!i[typeTag]
  })

  return _mixin
}

export const customUtil = createMixin({
  customUtil (event: any) {
    console.log(this)
  }
})

所以稍后可以使用 util 来装饰类,并且可以毫无问题地访问目标类的 this:

import { customUtil } from 'utils'

@customUtil
export default class ComponentClass extends Vue {
  someClassMethod() {
    this.customUtil() // successfully outputs the whole class in the console
  }
}

它会导致 tslinter 警告TS2339: Property 'customUtil' does not exist on type 'ComponentClass'.

问题
1. 是否有可能通过以某种方式“键入”由 mixin 实用程序分配给类的方法来解决 linter 问题
2. 如果有一种替代方法可以让实用程序函数/类可以毫无问题地访问它使用它们的类以及附加它们的简单方法?

4

1 回答 1

1

此问题已在其他线程中讨论过,也是TypeScript github 中的未决问题。但是有两种方法可以解决这个问题。

1:类型转换

您可以只进行类型转换this以忘记类 Context 或使用必要的信息对其进行增强。

import { customUtil } from 'utils'

@customUtil
export default class ComponentClass extends Vue {
  someClassMethod() {
    (this as any).customUtil(); // Now the linter will be fine but you will lose type safety
    (this as any as {customUtil: (event:any) => void}).customUtil(); // This works and you could/should extract the type. 
  }
}

但正如您所见,这并不理想。

2:真正的 TypeScript 混合

你可以使用真正的 TypeScript Mixins 而不是装饰器:

实用程序

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

// The Mixin
export function WithUtils<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    customUtil (event?: any) { // Actually, 'event' needs to be optional
      console.log(this)
    }
  };
}

// A Base class so you can just extend if needed
export const UtilsBase = WithUtils(class {});

零件

export default class ComponentClass extends WithUtils(Vue) {
  someClassMethod() {
    this.customUtil() // successfully outputs the whole class in the console
  }
}

类不从 Vue 扩展

export default class SomeClass extends UtilsBase {
  someClassMethod() {
    this.customUtil() // successfully outputs the whole class in the console
  }
}

// alternatively, you can use the Mixin with an unnamed class
export default class SomeClass extends WithUtils(class {}) {
  someClassMethod() {
    this.customUtil() // successfully outputs the whole class in the console
  }
}
于 2019-12-20T20:59:48.537 回答