Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript with Lodash: _.map(["123", " 234 "], _.trim) returns boolean[]?

I have an array of strings which have been split like so:

var searchValue = "600-800, 123, 180";
var groups = searchValue.split(","); // => ["600-800", " 123", " 180"]

(so there is potentially whitespace around the items) and I would like to remove the whitespace. I know that I can use Array.prototype.map with String.prototype.trim, but I would like a solution specifically using Lodash.

According to the docs, I should be able to say: _.map([' foo ', ' bar '], _.trim); and it will return ['foo', 'bar'], which is a string[].

However, TypeScript is barking at me. Here's what I'm seeing in my editor.

enter image description here enter image description here

I am running TypeScript 2.3.2 and Lodash 4.17.4

Strangely enough, if I say:

var values:string[] = _.map([' foo ', ' bar '], String.prototype.trim);

The TypeScript errors go away, but I get the following runtime error when searchValue is empty, and groups returns [""]:

TypeError: String.prototype.trim called on null or undefined

Things I have tried:

var values:string[] = _.map(_.filter(groups, group => group.indexOf("-") === -1), _.trim);
var values:string[] = _.map(_.filter(groups, function (group) { return group.indexOf("-") === -1; }), _.trim);
var values:string[] = _.map<string, (string?: string, chars?: string) => string>(_.filter(groups, function (group) { return group.indexOf("-") === -1 }), _.trim);
var values:string[] = _.map<string, (string?: string, chars?: string) => string[]>(_.filter(groups, function (group) { return group.indexOf("-") === -1 }), _.trim);
var values:string[] = _.map<string, (string: string, chars: string) => string>(_.filter(groups, function (group) { return group.indexOf("-") === -1 }), _.trim);
var values:string[] = _.map<string, (string: string) => string>(_.filter(groups, function (group) { return group.indexOf("-") === -1 }), _.trim);

All to no avail. I am at a loss. Is there something I am doing wrong here or is this possibly a Lodash/TypeScript bug?

like image 756
mhodges Avatar asked Jun 09 '17 23:06

mhodges


2 Answers

The following variation seems to work:

let values: string[] = _.map(['  foo  ', '  bar  '], str => _.trim(str))

Here's my guess as to why this is happening. There are many overloads for map, and you want to target this one:

map<T, TResult>(
    collection: List<T> | null | undefined,
    iteratee: ListIterator<T, TResult>
): TResult[];

where ListIterator is defined as:

type ListIterator<T, TResult> = (value: T, index: number, collection: List<T>) => TResult;

Thus you want the trimming function to match the above definition for ListIterator. But trim is actually defined as:

trim(
    string?: string,
    chars?: string
): string;

So when you do the following:

let values: string[] = _.map(['  foo  ', '  bar  '], _.trim);

You actually end up hitting a different overload, which is defined as:

    map<T, TObject extends {}>(
        collection: List<T>|Dictionary<T>|NumericDictionary<T> | null | undefined,
        iteratee?: TObject
    ): boolean[];

So when you switch to the following variation:

let values: string[] = _.map(['  foo  ', '  bar  '], str => _.trim(str))

Your trimming function str => _trim(str) conforms to ListIterator, even though it leaves out the last 2 parameters (index and collection).

like image 76
Frank Modica Avatar answered Sep 20 '22 17:09

Frank Modica


The problem is that _.trim can take two arguments:

trim(string?: string, chars?: string): string;

Which is incompatible with lodash's ListIterator type, in which the second argument, if present, is a number (the index).

Thus the compiler is falling back to a different typing for the _.map function, an obscure one which takes an object as the iteratee and returns an array of booleans ("returns true for elements that the properties of the given object, else false.")

Frank Modica's answer gives the correct solution.

like image 39
dbandstra Avatar answered Sep 18 '22 17:09

dbandstra