Flowtype:如何正确使用$ ObjMap使所有函数可链接?

时间:2019-07-07 23:21:38

标签: javascript types flowtype

我正在处理某些JS包,并且由于内部行为略有不同,因此想以index.js.flow的形式提供单独的流类型定义。

我具有用于创建组件定义的功能

function defineComponent(name, createFunc);

createFunc是一种获取元素并返回包含针对该组件的特定用户定义操作的对象的函数

因此您可以通过以下方式致电defineComponent

const loginForm = defineComponent("login form", () => {
  ...
  ...
  return {
    fillUsername: () => { ...doesn't matter what is return type... },
    fillPassword: () => { ...doesn't matter what is return type... }
  };
});

并且这些动作应该是可链接的,但是我不想让用户总是在每个用户定义的动作中提及返回类型,从而给用户带来负担。因此最终的链条应如下所示:

loginForm
  .fillUsername()
  .fillPassword()

因此在内部defineComponent将包装每个用户定义的动作,以实现链接功能:

function defineComponent(..., createFunc) {
  const actions = createFunc(...);

  return actions.map(action => {
    return (...args) => {
      action(...args);
      return actions;
    }
  })
}

我已经尝试过了(我的整个测试代码):

type ComponentType<T> = $ObjMap<T, <V>((V) => any) => V => ComponentType<T>>;

declare function defineComponent<T>(
  name: string,
  createFunc: () => T
): ComponentType<T>;

const Component = defineComponent("foo", () => {
  return {
    fooAction: () => {},
    barAction: () => {}
  };
});

Component.fooAction().barAction()

我继续使用流程的No errors!,但是当我执行类似的操作时,流程也没有显示错误

Component.fooAction.barAction()

此外,VS Code也不提供自动补全功能:/

谢谢!

1 个答案:

答案 0 :(得分:0)

这就是我能想到的。主要思想是我们将原始值类型V => mixed(即一元函数)映射到V => T(即相同的单个参数,但现在它返回操作映射T)。 / p>

function chainable<T: { [actionName: string]: Function }>(
  actions: T
): $ObjMap<T, <V>(V => mixed) => (V => T)> {
  return Object.keys(actions).reduce((newActions, actionName) => {
    newActions[actionName] = function(...args) {
      actions[actionName](...args);
      return this;
    }
    return newActions;
  }, {});
}

Try Flow

  

注意:我认为这不是完全类型安全的,因为this的类型实际上不是T。但是,我不认为Flow具有在this值中注释newActions类型的复杂性。此外,仅当所有操作均具有一元值功能时,此方法才有效。不幸的是,Flow没有词汇来描述可变参数泛型(有关这意味着什么以及如何使用的信息,请参见https://github.com/microsoft/TypeScript/issues/5453