I've been following an article about Lodash, Why using _.chain
is a mistake, and it highlights that you can remove the need for chain by using Flow
.
The example given is the following using chain
import _ from "lodash";
_.chain([1, 2, 3])
.map(x => [x, x*2])
.flatten()
.sort()
.value();
can be converted to the following using flow
import map from "lodash/fp/map";
import flatten from "lodash/fp/flatten";
import sortBy from "lodash/fp/sortBy";
import flow from "lodash/fp/flow";
flow(
map(x => [x, x*2]),
flatten,
sortBy(x => x)
)([1,2,3]);
However, when I implement this using TypeScript, I get the following error
Object is of type 'unknown'.ts(2571)
Is there any way to fix this so that TypeScript knows which types it is dealing with?
Type castingBoth Flow and TypeScript provide the ability to explicitly cast a variable from one type to another. With Flow, we cast using the symbol : , while in TypeScript we cast using the keyword as . // TypeScript let value = 1 as number; // Flow let value = 1; (value: number);
The lodash flow method works by calling the method and passing an array of functions that will be called on after another in order from the lowest index to the highest. For each call of each function the return value of the last function will be used for the argument value for the next and so forth.
As JavaScript/TypeScript is interpreted (note: The TypeScript code is transpiled to JavaScript which is then interpreted), when you use chain, typescript is aware of the type its dealing with (i.e. an array of numbers) as its comes first. However with flow, the type the flow functions are going to act on is specified last so it doesn't know what types it's going to use, hence the need of generics or another way of indicating the types that are going to be used. Also the linter generating this error is interpreting the code, so it doesn't know the types being dealt with when the input comes last when using "flow".
As you are using lodash with TypeScript, the functions you are using support generics to specify the types they are dealing with.
Hence you can state what is being inputted/outputted from the lodash functions as follows:
flow(
map<number, [number, number]>(x => [x, x * 2]),
flatten,
sortBy(x => x)
)([1, 2, 3]);
or as dareka suggests:
flow(
map((x: number) => [x, x * 2]),
flatten,
sortBy(x => x)
)([1, 2, 3])
But again this only works because we are indicating to the compiler what the type is that is going to be acted on.
An example of these code snippets working can be found here.
Note how the function inputs/outputs have been explicitly defined e.g. "map" indicates that the type it is going to iterate over is a number, and the result of the operation is a tuple.
The _.flow()
method generates a pointfree function. This prevents typescript from inferring the type of the input.
To solve this, assign the function to a variable, and add the function's type. When the functions parameters and the output are declared, typescript can infer the internal types of the functions inside flow.
After assigning the function to a variable, you can call it (sandobx):
const fn: (numbers: number[]) => number[] = flow(
map(x => [x, x*2]),
flatten,
sortBy(x => x)
);
const result = fn([1,2,3]);
Or use type assertion if you want to invoke the function immediately (sandbox):
type Fn = (numbers: number[]) => number[];
const result = (flow(
map(x => [x, x * 2]),
flatten,
sortBy(x => x)
) as Fn)([1, 2, 3]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With