【问题标题】:Annotating Higher Order Functions in Typescript在 Typescript 中注释高阶函数
【发布时间】:2019-10-02 22:24:33
【问题描述】:

我正在使用一个相当大的 JS 代码库,试图将其迁移到 typescript 并且遇到了注释特定高阶函数的问题...

doStuff() 接受fn1 作为参数并将其包装返回一个新函数,该函数接受除fn1 的第一个参数之外的所有参数。有点像这样:

const fn1 = (arg_1, arg_2, arg_3, arg_n) => { return 'Hello World' }

const doStuff = (fn) => (...args) => {
    argument1 = getSomeStuffHere()
    return fn(argument1, ...args)
}

const test = doStuff(fn1)
let result = test('arg2', 'arg3', 'arg4')

值得指出的是,有一个doStuff() 和很多fnX() 函数,其中包含各种类型组合的各种数量的参数。重要的是doStuff创建的函数输入正确,“any => any”不行!

经过一番折腾,我终于想出了这个:

// just for testing simplified example
type MagicObj = {}
const myMagicObject = {}

type Wrapped<T, R> =
    T extends [MagicObj, any, any, any, any] ? (a: T[1], b: T[2], c: T[3], d: T[4]) => R :
    T extends [MagicObj, any, any, any] ? (a: T[1], b: T[2], c: T[3]) => R :
    T extends [MagicObj, any, any] ? (a: T[1], b: T[2]) => R :
    T extends [MagicObj, any] ? (a: T[1]) => R :
    T extends [MagicObj] ? () => R :
    unknown;

const doStuff = <T extends any[], R>(fn: (...args: T) => R): Wrapped<T, R> => (...args) => fn(myMagicObject, ...args)

// testing examples
const fn1 = (obj: MagicObj, p1: string, p2: boolean, p3: number): string => { return 'Hello World' }
const fn2 = (obj: MagicObj, p1: number, p2: string) => { return 'Hello Mars' }
const fn3 = (obj: MagicObj, p1: boolean) => { return 'Hello The Moon' }

const test1 = doStuff(fn1)
// const test1: (a: string, b: boolean, c: number) => string 
let result1 = test1('str', true, 123) 

const test2 = doStuff(fn2)
// const test2: (a: number, b: string) => string
let result2 = test2(123, 'str')

const test3 = doStuff(fn3)
// const test3: (a: boolean) => string
let result3 = test3(true)

这似乎有点工作。 VSCodes 类型提示智能感知巫术向我展示了我对示例底部的 test1、2、3 和结果变量的期望/想要什么,但是 doStuff (...args) =&gt; fn(myMagicObject, ...args) 返回的函数,无论我如何尝试注释它,报告错误Type '(...args: any[]) =&gt; any' is not assignable to type 'Wrapped&lt;T, R&gt;'.ts(2322)

有什么想法可以做到这一点吗?

【问题讨论】:

    标签: typescript higher-order-functions


    【解决方案1】:

    如果我理解你想要做什么,我会像这样输入doStuff()

    const doStuff = <T extends any[], R>(
      fn: (magicObject: MagicObj, ...args: T) => R
    ): ((...args: T) => R) => (...args) => fn(myMagicObject, ...args);
    

    这表示从第一个参数类型为MagicObj 的任何函数到另一个相同类型但删除了MagicObj 参数的函数的转换。通过使用rest tuples,它应该适用于任意数量的参数。

    这对你有用吗?好的,希望有帮助。祝你好运!

    Link to code

    【讨论】:

    • 效果很好,这是我需要的示例。谢谢你。我还调整了最终的“R”以允许对包装器进行更改,如果 fn 返回未定义,则将返回 magicObject:因此最终的 R 变为“(R extends void ? MagicObject : R)”。再次感谢。
    猜你喜欢
    • 2012-10-25
    • 2020-05-17
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    • 2022-07-01
    相关资源
    最近更新 更多