【问题标题】:How to let typescript understand the existence of static members that are dynamically added to a class?如何让打字稿了解动态添加到类中的静态成员的存在?
【发布时间】:2019-12-05 19:51:45
【问题描述】:

我有一个 QueryBuilder,并希望将其所有方法作为静态成员呈现在 Model 类中。

显然,这是有效的 Javascript 代码。但是,我怎么能让打字稿理解Model上那些静态方法的存在呢?

function queryable (target: any) {
  for (const prop in target.prototype.builder) {
    if (prop === 'constructor' || target.prototype.hasOwnProperty(prop)) {
      continue;
    }
    target[prop] = (...args: any[]): any => {
      return target.prototype.builder[prop](...args);
    };
  }
}

class QueryBuilder {
  where(prop: string, value: string) {
    console.log(prop, value);
    console.log("I've presented on model class as static method.");
  }
  // ...
}

@queryable
class Model {
  get builder () {
    return new QueryBuilder();
  }
  // ...
}


// calling static where
Model.where('color', 'red');

类型“typeof Model”上不存在属性“where”。

Typescript Playground

【问题讨论】:

    标签: typescript


    【解决方案1】:

    您应该注意的问题是TypeScript#4881,但从目前 (3.5.1) 开始,装饰器 无法更改它正在装饰的类的类型。

    话虽如此,您仍然可以以相同的方式定义装饰器,但显式调用它而不是作为装饰器,因为许多使用 redux 流行的connect 装饰器的打字稿项目必须这样做。

    class _Model {
      get builder () {
        return new QueryBuilder();
      }
    
      // ...
    }
    const Model = queryable(_Model);
    

    或者如果它是一个模块的主要导出,你可以使用一个名称:

    class Model {
      get builder () {
        return new QueryBuilder();
      }
    
      // ...
    }
    export default queryable(Model);
    

    然后诀窍是更好地注释装饰器以教打字稿queryable对其target所做的所有添加:

    function queryable<T extends new (...args: any[]) => {builder: any}>(target: T): InstanceType<T>['builder'] & (typeof target) {
      for (const prop in target.prototype.builder) {
        if (prop === 'constructor' || target.prototype.hasOwnProperty(prop)) {
          continue;
        }
        (target as any)[prop] = (...args: any[]): any => {
          return target.prototype.builder[prop](...args);
        };
      }
      return target as any
    }
    

    Playground link

    【讨论】:

    • 好点。我不介意明确的实施。然而,实际上,Model 类也接受一个类型,即class Model&lt;T&gt; extends ...,我想知道在你调用queryable(Model) 的示例中如何解决这种情况?
    • 应该没问题。参数的类型只是一个具有 builder 属性的类。返回类型表示它是同一个类加上构建器字段。
    • +1。因此,在这里解决我最初的问题,以帮助 Typescript 理解动态添加的静态成员将是一个连接器,如 const connect = &lt;T extends new () =&gt; {builder: any}&gt;(target: T): InstanceType&lt;T&gt;['builder'] &amp; (typeof target) =&gt; target; 然后 const Model = connect(OriginalModel);
    【解决方案2】:

    由于 Typescript 是一种强类型语言,它总是检查类型使用是否与类型接口匹配。这种检查发生在编译期间,当 Typescript 不知道你要添加一个新方法时。因为您在运行时添加它。调用这种方法的唯一方法是转换为像any 这样的通用方法:

    (<any>Model).where('color', 'red');
    

    这就像你对 Typescript 说你确定方法存在并且 transpiller 不应该担心它。

    【讨论】:

    • 谢谢伙计。这是摆脱错误的一种解决方法,但我宁愿不施放而是改变目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-26
    • 2016-06-13
    • 1970-01-01
    • 2011-07-05
    • 1970-01-01
    • 2019-11-07
    • 2017-12-06
    相关资源
    最近更新 更多