How should the lodash flow function be typed in typescript?

若如初见. 提交于 2019-12-01 05:04:30

I don't believe you can write that definition.

If we look at the lodash type declaration file they don't try to express that relationship.

interface LoDashStatic {
    flow<TResult extends Function>(...funcs: Function[]): TResult;
}

But that alone isn't reason enough to discount the possibility. The authors may have just overlooked something, so let's keep thinking about it.

The relationship between an individual chain of functions is something you can represent. You've done so in your example above. You could create manual versions of that same idea for several lengths of parameters but that's because you're setting up a situation where the length of the chain is known and you can grant individual type information.

If we are to handle the case of variable length parameters, we must treat the parameters as a Collection. Because all variables must have a single (though possibly parameterized) type so too must this collection object. However, the types of the various functions do not align. (param:A) => B is not the same type as (param:B) => C and cannot be stored in the same well typed container (barring union types but those won't scale either).

In situations where you want to retain type information on a list of parameters like this you usually define the composition function on two parameters and apply it across a number of functions. This is how type information is retained in promises, for example. For this to work you still need to have each individual parameter spelled out. It just makes it so by the end you've got the proper output type. That said, in most cases this is what you want so it's all good.

If lodash were written in a well typed functional language, that flow function probably wouldn't exist. I imagine it would instead have been written as a piped composition object.

UPDATE: What do I mean when I say a "piped composition object"? Something like this, perhaps:

class FunctionComposer<T,V> {
    constructor(protected func: (param: T) => V) { }

    public compose<X>(newFunc: (param:V) => X) {
        return new FunctionComposer((x: T) => newFunc(this.func(x)));
    }
    public out() {
        return this.func;
    }
}

let composedFunc = new FunctionComposer((x: number) => x * 2)
    .compose(x => x.toString())
    .out();

// composedFunc has type (param:number) => string
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!