如何在TypeScript中创建通用高阶函数

时间:2017-06-23 12:15:59

标签: typescript generics

我不确定如何创建一个通常会将行为添加到另一个函数的函数。这就是我要做的事情:

/**
 * Creates a function that calls originalFunction, followed by newFunction.
 * The created function returns the value returned by the original function
 * returned by the original function
 * @param originalFunction Function to be called first
 * @param newFunction Function to be called second
 */
function callAfter<T extends Function>(originalFunction: T, newFunction: T): any {
   return function() {
        const result = originalFunction.apply(this, arguments);
        newFunction.apply(this, arguments);
        return result;
   };
}

上述内容并未使用以下消息进行编译

  

TS2322:输入&#39;()=&gt;任何&#39;不能分配给&#39; T&#39;

我能做些什么来保持类型安全吗?现在我有了它&#34;工作&#34;让它返回any

我进一步尝试了,并提出以下

/**
 * Creates a function that calls originalFunction, followed by newFunction. The created function returns the value
 * returned by the original function
 * @param originalFunction Function to be called first
 * @param newFunction Funct
 */
function callAfter<R, T extends () => R>(originalFunction: T, newFunction: T): T {
    return <T> function () {
        const result: R = originalFunction.apply(this, arguments);
        newFunction.apply(this, arguments);
        return result;
    };
}

这个问题是它对于返回void的函数不起作用(这是我此刻真正关心的用例)

这是我尝试编写的代码,但希望使用通用的高阶函数来扩展功能。

/**
 * Component can use this to have subscriptions automatically
 * removed when the component is removed from the DOM
 */
class SubscriptionTracker {
    private subscriptions: Subscription[] = [];

    constructor(destroyable: OnDestroy) {
        destroyable.ngOnDestroy = callAfter(destroyable.ngOnDestroy, () => {
            this.subscriptions.forEach((subscription) => subscription.unsubscribe());
        });
    }

    subscribe<T>(observable: Observable<T>, observer: PartialObserver<T>): Subscription {
        const subscription = observable.subscribe(observer);
        this.subscriptions.push(subscription);
        return subscription;
    }

    unsubscribe(subscription: Subscription) {
        subscription.unsubscribe();
        const indexOfSubscription = this.subscriptions.indexOf(subscription);
        if (indexOfSubscription == -1) {
            throw new Error('Unsubscribing to untracked subscription');
        }
        this.subscriptions.splice(indexOfSubscription, 1);
        return subscription;
    }
}

1 个答案:

答案 0 :(得分:0)

据我所知,你只需要对返回类型和参数有更好的约束。我们可以为每个函数定义一个适当的函数类型,而不是让仿函数采用相同类型T的两个函数。

function callAfter<O>(f: (...any) => O, f2: (...any) => void): (...any) => O {
    return (...args) => {
        let r = f(...args);
        f2(...args);
        return r;
    }
}

一些例子:

function  double(x) { return x + x; };

function yell(x) {
    console.log(`${x}!!!`);
}

let doubleLoud = callAfter(double, yell);
let x = doubleLoud(4501);
console.log(`doubleLoud(4501) = ${x}`);

它也适用于在任何一方返回void的函数,甚至包含多个参数的函数:

callAfter(yell, yell)("Ni");

let lreluLoud = callAfter(function lrelu(x, alpha) {
    if (x < 0) x *= alpha;
    return x;
}, yell);

console.log(`lreluLoud(-20, 0.2) = ${lreluLoud(-20, 0.2)}`);