typescript - 使用 TypeScript 进行部分应用

标签 typescript

我喜欢使用对象文字作为函数参数,因为它允许我标记参数名称。我认为会有一种类型安全的通用方法来对这些类型的函数进行部分函数应用。假设一个函数采用 X 类型的参数。然后我通过另一个名为“partial”的函数运行该函数,我在其中提供 X 的 Partial,它可以返回一个只需要缺失值的新函数。通过下面的示例最容易看出。这里的好处是,使用我的“部分”函数,我可以提供任意数量或参数的组合,并获得一个结果函数,该函数清楚地表明仍然需要什么。

function partial<Args, Fixed extends Partial<Args>, Result>(fn: (args: Args) => Result, fixed: Fixed) {
    type Unspecified = { [P in Exclude<keyof Args, keyof Fixed>]: Args[P] };
    const result = (args: Unspecified) => {
        const combined = {};
        Object.assign(combined, fixed, args);
        return fn(combined as Args);
    };
    return result as ({} extends Unspecified ? () => Result : (args: Unspecified) => Result);
}

interface AddThreeNumbersArgs {
    a: number;
    b: number;
    c: number;
    caption: string;
}

function addThreeNumbers(args: AddThreeNumbersArgs) {
    return `${args.caption} ${args.a + args.b + args.c}`;
}

test("fix one number and the caption", () => {
    const f = partial(addThreeNumbers, { b: 10, caption: "The answer is:" });
    const result = f({ a: 1, c: 25 });
    expect(result).toBe("The answer is: 36");
});

这一切都适用于上面的 AddThreeNumbers 示例。但是当函数参数是通用的时它不起作用 - 见下文 - 我不知道为什么。相反,partial 的结果是一个没有参数而不是缺失部分的函数。有 TypeScript 大师知道为什么吗?

interface ConcatenateArrayArgs<TItem> {
    first: TItem[],
    second: TItem[]
}

function concatenate<T>(args: ConcatenateArrayArgs<T>) {
    return [...args.first, ...args.second];
}

test("concatenate", () => {
    const result = concatenate({ first: [1, 2, 3], second: [4, 5, 6] });
    expect(result).toEqual(expect.arrayContaining([1, 2, 3, 4, 5, 6]));
});

test("fix first array in concatenate to array of numbers", () => {
    const f = partial(concatenate, { first: [1, 2, 3] });
    // expect f to take a { second: [4,5,6] } here but instead
    // f is a function with no arguments
});

最佳答案

我认为您遇到了 Microsoft/TypeScript#9366 中的问题; TypeScript 对涉及 higher-rank function types 的类型推断没有很好的支持.更新:在 TS3.4 中添加了对 inferring higher order function types 的支持不幸的是,它不够通用,无法在这里为您提供帮助;编译器现在可以传播一些通用类型参数从输入函数到输出函数(例如,如果 concatenate 是通用的,那么 partial(concatenate, ... ) 也将是通用的),但它不能任意指定输入函数中的通用类型参数(例如,为类型参数插入 numberconcatenate 中使输出函数非泛型)。因此,例如 microsoft/TypeScript#25256仍然是设计限制。

我能想到的唯一解决方法是让您在某处显式指定类型。例如,您可以将回调函数的级别降低到非通用级别,如下所示:

const f = partial(
  concatenate as (x: ConcatenateArrayArgs<number>)=>number, 
  { first: [1, 2, 3] }
);

然后推理如您所愿。或者您可以在对 partial() 的调用中显式指定类型参数,以便 it 本质上是非泛型的,并且编译器不会推断出错:

const r = partial<
  ConcatenateArrayArgs<number>,
  { first: number[] },
  number[]
>(concatenate, { first: [1, 2, 3] });

这些都有效,但不是特别令人满意。希望至少能为您指明正确的方向。祝你好运!

关于typescript - 使用 TypeScript 进行部分应用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50400120/

相关文章:

javascript - 使用 TypeScript 设置 window.location

typescript - Intellij TypeScript 变量名自动完成

angular - 如何在 Angular 中使用 JSON 数组值填充 HTML 表?

javascript - 来自 NodeJs Crypto 和 CryptoJS 库的不同加密值

javascript - TS2339 : Property 'includes' does not exist on type 'string'

http - 使用模拟后端测试 Angular2 服务

html - 如何在ionic 3中使用@output

typescript - CDK Step Functions - 如何创建循环

javascript - 在同一行中分配变量和更改属性

javascript - 获取扩展对象的 TypeScript 代码补全