【问题标题】:How to create a util for the class如何为类创建一个 util
【发布时间】:2020-04-13 06:05:33
【问题描述】:

我做了什么
我最初的方法是使用以下返回类装饰器的 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. 如果有另一种方法可以让实用函数/类可以毫无问题地访问它们所使用的类的 this 并以简单的方式附加它们?

【问题讨论】:

    标签: typescript vue-class-components


    【解决方案1】:

    这个问题已经在other Threads 讨论过,也是pending issue in the 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 Mixins

    你可以使用真正的 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
      }
    }
    

    【讨论】:

    • Pascal 非常感谢您的帮助,我带我进行了一些研究,但最终我回到了您在这里建议的想法。祝你一切顺利:-)
    • 只对Cannot find name 'T'.type Constructor&lt;T = {}&gt; = new (...args: any[]) =&gt; T 感到小问题,您能建议一个可能的解决方法吗? :-)
    • 我曾尝试声明declare module 'T' { const T: any export = T },但现在运气不错:)
    • @volna 很高兴它有帮助! :) 对于您的问题:T 不是模块而是通用的。它应该按原样编译。您使用的是哪个 TypeScript 版本?
    • 3.5.3 绝对可以处理泛型 - TypeScript Playground 可以追溯到 2.4.1 甚至可以处理它......奇怪。你有没有以某种方式改变那条线?当你将它复制粘贴到 TypeScript Playground 时它会说什么?由于我发布的代码在操场上确实有效,所以我有点想不通......
    猜你喜欢
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 2022-12-13
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多