4

I was wondering why Typescript complains with the following error:

(22,28): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
  Type argument candidate 'string' is not a valid type argument because it is not a supertype of candidate 'number[]'.

when I use the zip function with the following arguments:

let ranges = ["0-100", "100-200", "200-300", "300-400", "400-500"];
let arrays = [[19.99, 49.99, 49.99, 49.99, 29.99, 29.99, 9.99, 29.99, 34.99, 34.99, 59.99], [149.99, 179.99, 129.99, 149.99, 129.99, 199.99, 129.99], [209.99, 249.99, 292.99, 279.99, 219.99]];

let result = _.zip(ranges, arrays);

However, if I use _.zipObject, the error disappears.

In case it is important, I installed the type information using typings install lodash --save.

UPDATE 2

I think zip doesn't like to receive arguments with different types. In this case, ranges is of type string[] and arrays of type number[].

UPDATE

I was wrong. I changed the values of arrays to use strings, but now I get this slightly different error:

(24,28): error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage. Consider specifying the type arguments explicitly.
  Type argument candidate 'string' is not a valid type argument because it is not a supertype of candidate 'string[]'.

Maybe there is something related to the nested arrays in the variable arrays?

3
  • what about explicitly specifying union type as generic parameter? _.zip<number|string>(...) Commented Jul 23, 2016 at 21:34
  • Thanks. Let me try. Commented Jul 23, 2016 at 22:17
  • No, still not working. I guess you meant to explicitly type what is the expected output for _.zip. I think that would be an array of arrays. Commented Jul 23, 2016 at 22:23

2 Answers 2

5

You are correct that zip does not like arguments of different types. As defined here...

zip<T>(...arrays: List<T>[]): T[][];

This basically means that all of the parameters must be an array of the same type. However, when you changed arrays to strings I suspect you had something like...

let ranges = ["0-100", "100-200", "200-300", "300-400", "400-500"];
let arrays = [["19.99", "49.99",...], ["149.99", ...], ...];

These are still not the same type. ranges is a one dimensional array of strings (array of string) and arrays is a two dimensional array of strings (array of string[]). A valid set of inputs would be something like...

let ranges = ["0-100", "100-200"];
let arrays = ["19.99", "49.99"];

Here both types are arrays of strings. However, I suspect this is not what you want. Is your desired output something like the following?

[["0-100", ["19.99", ...]], ["100-200", ["149.99", ...]], ...]

If so then you can simply do...

_.zip<any>(ranges, arrays);

This tells typescript to force T to be any so the function definition becomes...

zip(...arrays: List<any>[]): any[][];

Example:

let ranges = ["0-100", "101-200"];
let arrays = [[0, 1, 2], [3, 4, 5]];
_.zip<any>(ranges, arrays);
//[ [ '0-100', [ 0, 1, 2 ] ], [ '101-200', [ 3, 4, 5 ] ] ]

UPDATE: As mentioned in the comments, you could also do...

_.zip<string|number[]>(ranges, arrays);
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you. Excellent answer. I was confused about the types used in the definitely typed definitions. A couple of questions: What is the List type in the context of List<T>[]? How do you read <string|number[]>?
@RobertSmith <string|number[]> - Either string or array of numbers, <(string|number)[]> - either array of strings or array of numbers. See Union Types
List is actually defined elsewhere in the lodash type definitions. It is intended to be slightly more generic than arrays to include jQuery objects. You could think of it as <Array|jQueryObject> except there is no common type for jQuery objects so they defined it a little differently (e.g. anything indexable with a length property). The ... is a way of saying, allow zero or more arguments. So this function can take 1, 2, 3, or however many items you want, each item needs to be a List<T>.
So basically it is saying, give me zero or more arrays (or jQuery objects), each of which can be cast to some common type T. The actual zip implementation will receive these List objects as an array which is why it is List<T>[] and not List<T>. It is not saying the arguments are each List arrays (which would be a 2-dimensional array) but rather that all of the List objects will be grouped into an array for the implementation.
Thank you. I had to read it a few times, but now it is clear what it means.
1

There is now a form of _.zip which allows you to specify, as type parameters, the types contained within the arrays.

zip<T1, T2>(arrays1: List<T1>, arrays2: List<T2>): Array<[T1 | undefined, T2 | undefined]>;

so you would be able to write

_.zip<string, number[]>(ranges, arrays);

This would return Array<[string | undefined, number[] | undefined]> and would allow you to avoid using any (which defeats the purpose of using TypeScript)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.