flowtype:如何使用参数转发注释高阶函数

标签 flowtype

我有一个函数foo :

function foo(a: string, b: string, c: number, d: boolean): Promise<Result> {
  return new Promise(resolver => {
    ...
  });
}

// use foo
foo('hello', 'world', 3, true).then(...);

和一个高阶函数,它采用函数,然后是使用柯里化(Currying)的参数:

function hof(func: Function) {
  return async function (...args: any[]) {
    // forward the args to the func and get the result
    const result = await func(...args);
    // do something else with the result
  }
}

// use foo with higher-order function `hof`
hof(foo)('hello', 'world', 3, true);
         ^^^^^^^^^^^^^^^^^^^^^^^^^ lack type annotations

使用方法flowtype注释 hof 的整个部分功能:
  • 霍夫接口(interface)
  • 转发参数 ...args : 使用 any[]将丢失原始类型
  • 柯里化(Currying)函数的返回类型
  • 最佳答案

    TL;DR

    我仍在寻找更好的选择,但到目前为止这对我有用:

    function foo(a: string, b: string, c: number): string {
      return 'Yay!'
    }
    // Alternatively, interface Arguments {
    type Arguments = {
        <A>((A) => any): [A],
        <A, B>((A, B) => any): [A, B],    
        <A, B, C>((A, B, C) => any): [A, B, C],     
        <A, B, C, D>((A, B, C) => any): [A, B, C, D],
    }
    
    function hof <T: Function> (t: T): (...$Call<Arguments, T>) => void {
      return (...args) => undefined;
    }
    
    hof(foo);
    hof(foo)("This", "works", 2);
    // hof(foo)("this", "should't", "work");
    // hof(foo)("Correctly", "fails", 2, "when there are extra args");
    

    我遇到了类似的问题。也就是说,键入一个函数,该函数接受一个具有特定参数集的函数并返回一个接受相同参数集的新函数,但我找不到明确的答案。

    加长版

    这是研究流类型问题并尝试错误的结果。 These are all the variations I tried that work in some degree

    function foo(a: string, b: string, c: number): string {
      return 'Yay!'
    }
    
    // Attempt 1:
    // Taken from 
    // https://github.com/facebook/flow/issues/4672#issuecomment-377649789
    
    declare function arguments<A>((A) => any): [A]
    declare function arguments<A, B>((A, B) => any): [A, B]
    declare function arguments<A, B, C>((A, B, C) => any): [A, B, C]
    declare function arguments<A, B, C, D>((A, B, C, D) => any): [A, B, C, D]
    type Arguments<T> = $Call<typeof arguments, T>
    
    function hof1 <T: Function> (t: T): (...Arguments<T>) => void {
      return (...args) => undefined;
    }
    
    const augmentedFn = hof1(foo)("This", "works", 2);
    // const augmentedFn2 = hof1(foo)("this", "should't", "work");
    // const augmentedFn2 = hof1(foo)("Also", "check", 2, "number of args");
    
    // Attempt 2: using array of mixed.
    // This one doesn't seem to have limit to the arguments you can provide,
    // but it doesn't seem to fail when extra args are passed.
    // Notice that the return type is specified in the function signature
    
    function hof2 <T: Array<mixed>>(func: (...T) => any): (...args: T) => any {
        return (...args) => null;
    }
    
    hof2(foo)("2", "2", 1);
    // hof2(foo)(1, "this fails correctly", 1);
    hof2(foo)("2", "2", 1, "This one has extra arg, but this method can't  limit them");
    
    // If we define the generic return type in the returned function, flow breaks
    function hof2broken <T: Array<mixed>>(func: (...T) => any) {
        return (...args: T) => null;
    }
    // hof2broken(foo)("This should work", "but fails with a cryptic message", 1);
    
    // Attempt 3, using generics to capture the tuple values.
    // It requires us to declare one generic for each arg in any function
    // that uses them. Notice that I have to redeclare all the A, B, C and D generic
    // types just to pass them to the tuple type.
    
    type genericTuple<A, B, C, D> = [A] | [A, B] | [A, B, C] | [A, B, C, D]
    
    function hof3<A, B, C, D, T: genericTuple<A, B, C, D>>(func: (...T) => any): (...args: T) => any {
      return (...args) => null;
    }
    
    hof3(foo)("This", "works", 2);
    // hof3(foo)("this", "should't", "work");
    hof3(foo)("Also", "fails", 2, "to check the argument's length");
    // hof3(foo)("Also", "fails", 2, "to check the argument's length", "until it exceeds the number of possible args");
    
    // Attempt 4
    // It seems to be possible to ommit the generics by using tuples of 'any'
    // This seems to have the same issues of not checking for extra unwanted args
    
    type anyTuple = [any] | [any, any] | [any, any, any] | [any, any, any, any]
    
    function hof4<T: anyTuple>(func: (...T) => any): (...args: T) => any {
      return (...args) => null;
    }
    
    hof4(foo)("This", "works", 2);
    // hof4(foo)("this", "should't", "work");
    hof4(foo)("Also", "fails", 2, "to check the argument's length");
    
    // Attempt 5:
    // Trying to extract the type calculated in 
    // https://github.com/facebook/flow/issues/4672#issuecomment-377649789
    // without getting redeclaration warnings
    // This works in the 'try' console, but doesn't in the 0.80.0 version
    // in Mac OS, so I can't stick with it
    // I think that this working on the 'try' console is probably a bug
    
    type ArgsFrom<T> = $Call<
      (<A>((A) => any) => [A]) &
      (<A, B>((A, B) => any) => [A, B]) &
      (<A, B, C>((A, B, C) => any) => [A, B, C]) &
      (<A, B, C, D>((A, B, C, D) => any) => [A, B, C, D]), T>
    
    // type Arguments2<T> = $Call<argtest, T>  
    
    function hof5 <T: Function> (t: T): (...ArgsFrom<T>) => void {
      return (...args) => undefined;
    }
    
    let testVar = hof5(foo);
    hof5(foo)("This", "works", 2);
    // hof5(foo)("this", "should't", "work");
    // hof5(foo)("Correctly", "fails", 2, "when there are extra args");
    
    // Attempt 6: I read somewhere that overloading is supported
    // on interfaces, so I gave it a try. It seems to work and I will stick with it.
    
    type Arguments3 = {
        <A>((A) => any): [A],
        <A, B>((A, B) => any): [A, B],    
        <A, B, C>((A, B, C) => any): [A, B, C],     
        <A, B, C, D>((A, B, C) => any): [A, B, C, D],
    }
    
    function hof6 <T: Function> (t: T): (...$Call<Arguments3, T>) => void {
      return (...args) => undefined;
    }
    
    hof6(foo);    
    hof6((a: string, b: boolean) => null)("Also works", true); 
    hof6(foo)("This", "works", 2);
    hof6(foo)("this", "should't", "work");
    hof6(foo)("Correctly", "fails", 2, "when there are extra args");
    
    // Yay! Works and kind of makes sense 
    

    关于flowtype:如何使用参数转发注释高阶函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41338664/

    相关文章:

    javascript - 如何将流和 typescript 类型添加到 NPM 模块

    javascript - 类型细化 flowtype 中的 `any` 类型

    javascript - 内联 webpack 加载器的流类型问题

    javascript - 流式返回函数时不保留泛型函数参数

    javascript - 流(类型)不尊重我对类属性所做的手动检查

    flowtype - Canvas 元素的正确类型

    reactjs - ESLint 报告流类型全局类型的 no-undef 错误

    flowtype - 如何为具有通用类型的箭头功能编写流类型

    javascript - 如何在 Javascript(和 Flow)中使用条件访问根据键对对象列表进行排序?

    javascript - 如何注释作为对象属性的函数?