Typescript: Typescript parsed spread operator in function call problem

Created on 11 Nov 2018  路  6Comments  路  Source: microsoft/TypeScript

I have a piece of code as is:

function add(a: number, b: number, c: number) {
  return a + b + c;
}

const hexagons = [1, 6, 15];

const result = add(...hexagons);

And VS Code complains about:
[ts] Expected 3 arguments, but got 0 or more.

I haven't found if it is necessary to set some config option or this is a bug in the VSCode TS parser itself.

But it is valid code in Chrome:

function add(a, b, c) {
  return a + b + c;
}
const hexagons = [1, 6, 15];
const result = add(...hexagons);

So I think VSCode is wrongly parsing TS.

Duplicate In Discussion Suggestion

Most helpful comment

@gilbertoalbino The actual problem with your code is that hexagons has the inferred type number[]. So ...hexagons could be 0, 1, 2, 3, ... arguments, even though you must pass 3.

@mjbvz 's point is that because you could have removed an item from it (since it's a number[]), it's not sound to permit you to spread it onto your function (or you'd pass an undefined where a number was expected).

Today, you can fix this just by adding a type annotation

function add(a: number, b: number, c: number) {
  return a + b + c;
}

const hexagons: [number, number, number] = [1, 6, 15];

const result = add(...hexagons);

If you're going to be using this type frequently, I recommend a type definition:

type Triplet = [number, number, number]

const hexagons: Triplet = [1, 6, 15];

It will probably be a while before a complete auto-narrowing solution can be made for object types.

All 6 comments

Doing this with array types in general is not valid typescript because you can easily have:

function add(a: number, b: number, c: number) {
  return a + b + c;
}

const hexagons = [1, 6, 15];

hexagons.pop();

const result = add(...hexagons);

Not sure if there are existing TS feature requests that would allow this to be supported

Control flow narrowing of singleton and literal types would about cover it, I think. #16896 specifically mentions what seems to be exactly this.

@mjbvz in plain Javascript, it is valid Javascript, Why wouldn't it be in TypeScript!????

Unfortunately, there are a few issues which make extending #16896 soundly extend to TypeScript beyond string/number literals (that is, making it apply to objects/arrays).

When a narrowed value is assigned to, the value gets widened as appropriate, so that the following program behaves as expected:

let x = "foo";

if (x !== "foo") {
    throw new Error("narrow x");
}

x; // type: "foo"

x = someString();

x; // type: string

The problem is that today, the following piece of code is also valid:

function pop(xs: number[]) {
    xs.pop();
}
const x: [number, number, number] = [1, 2, 3];
push(x);

x; // type: [number, number, number], which is unsound

This is a problem for soundness even ignoring the auto-narrowing condition. But because right now the only way to get fixed-sized tuple types is to explicitly give type annotations, this is not a wide-spread problem.

But automatically narrowing would make fixed-size tuple types much more common (every single array literal!). So you'd potentially end up with arrays that are definitely-a-particular-size all over almost every code base, except that they're totally not.

@gilbertoalbino The actual problem with your code is that hexagons has the inferred type number[]. So ...hexagons could be 0, 1, 2, 3, ... arguments, even though you must pass 3.

@mjbvz 's point is that because you could have removed an item from it (since it's a number[]), it's not sound to permit you to spread it onto your function (or you'd pass an undefined where a number was expected).

Today, you can fix this just by adding a type annotation

function add(a: number, b: number, c: number) {
  return a + b + c;
}

const hexagons: [number, number, number] = [1, 6, 15];

const result = add(...hexagons);

If you're going to be using this type frequently, I recommend a type definition:

type Triplet = [number, number, number]

const hexagons: Triplet = [1, 6, 15];

It will probably be a while before a complete auto-narrowing solution can be made for object types.

@Nathan-Fenner indeed using type annotation goes away with the error :

[ts] Expected 3 arguments, but got 0 or more.

But I think I haven't understood yet why this parse error is being triggered.

Was this page helpful?
0 / 5 - 0 ratings