Typescript: Wrong type inferred by returning a tuple

Created on 9 Jul 2018  ·  8Comments  ·  Source: microsoft/TypeScript

TypeScript Version: 3.0.0-dev.20180707

Code

  const a: Array<[string, boolean]> = ["foo", "baz"].map(item => [item, !!item]);
  const b: Array<[string, boolean]> = ["foo", "baz"].map(item => { const value: [string, boolean] = [item, !!item]; return value;} );

Expected behavior:
Both expressions should be allowed
Actual behavior:
assignment for b works, assignment for a doesn't, as the compiler starts infering that the type for [item, !!item] is Array.

In Discussion Suggestion

Most helpful comment

Another example where a tuple inference would be desirable is in f0 below:

declare function f0<T extends any[]>(args: T): T;
declare function f1<T extends any[]>(...args: T): T;

f0(["a", 1]); // (string | number)[]
f1("a", 1); // [string, number]

This is currently blocking a typesafe implementation of Promise.all without overloads.

All 8 comments

It's not wrong, it's just less precise than it should be.

I think this is fixable by improving the contextual type -> return type expression inference?

Another example where a tuple inference would be desirable is in f0 below:

declare function f0<T extends any[]>(args: T): T;
declare function f1<T extends any[]>(...args: T): T;

f0(["a", 1]); // (string | number)[]
f1("a", 1); // [string, number]

This is currently blocking a typesafe implementation of Promise.all without overloads.

👍

Yes, the update to tuples to allow for f1 in @jscheiny's comment is great! Unfortunately for some of the Promise functions it doesn't work since the argument is actually an array argument, not rest arguments.

This is not only related to functions. Variables are affected too:

    let test1: [number, string];
    const a = [42, ''];

    test1 = a; // error

Type '(string | number)[]' is not assignable to type '[number, string]'.
Property '0' is missing in type '(string | number)[]'.

@rrousselGit that is the intended behavior, because this code is legal:

    let test1: [number, string];
    const a = [42, ''];
    a[0] = "hello"; // Does not affect type system

    test1 = a; // Unsound to allow

This issue prevents normal creation of ES6 maps. This valid JS:

var kvArray = [['key1', 'value1'], ['key2', 'value2']];
var myMap = new Map(kvArray);

Doesn't compile due to Argument of type 'string[][]' is not assignable to parameter of type 'ReadonlyArray<[any, any]>'..

But if you just cast it to the right type it works fine:

const kvArray = [['key1', 'value1'], ['key2', 'value2']]
const myMapFromAny = new Map(kvArray as Array<[string, string]>)

At least I think this is the same issue. Didn't want to make a duplicate issue.

@rrousselGit By default, array literal declarations are given an array type, not a tuple type. There is a proposal to change this, and if you have a strong opinion to share I'd recommend chiming in because it is still in the feedback stage: #16896

+1
for different return types for different promises in promise.all

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kyasbal-1994 picture kyasbal-1994  ·  3Comments

dlaberge picture dlaberge  ·  3Comments

seanzer picture seanzer  ·  3Comments

fwanicka picture fwanicka  ·  3Comments

MartynasZilinskas picture MartynasZilinskas  ·  3Comments