So there is a little clause you may have missed:
Type checking requires spread elements to match up with a rest parameter.
Without Rest Parameter
But you can use a type assertion to go dynamic... and it will convert back to ES5 / ES3 for you:
function foo(x:number, y:number, z:number) {
console.log(x,y,z);
}
var args:number[] = [0, 1, 2];
(<any>foo)(...args);
This results in the same apply function call that you'd expect:
function foo(x, y, z) {
console.log(x, y, z);
}
var args = [0, 1, 2];
foo.apply(void 0, args);
With Rest Parameter
The alternative is that it all works just as you expect if the function accepts a rest parameter.
function foo(...x: number[]) {
console.log(JSON.stringify(x));
}
var args:number[] = [0, 1, 2];
foo(...args);
Answer from Fenton on Stack OverflowSo there is a little clause you may have missed:
Type checking requires spread elements to match up with a rest parameter.
Without Rest Parameter
But you can use a type assertion to go dynamic... and it will convert back to ES5 / ES3 for you:
function foo(x:number, y:number, z:number) {
console.log(x,y,z);
}
var args:number[] = [0, 1, 2];
(<any>foo)(...args);
This results in the same apply function call that you'd expect:
function foo(x, y, z) {
console.log(x, y, z);
}
var args = [0, 1, 2];
foo.apply(void 0, args);
With Rest Parameter
The alternative is that it all works just as you expect if the function accepts a rest parameter.
function foo(...x: number[]) {
console.log(JSON.stringify(x));
}
var args:number[] = [0, 1, 2];
foo(...args);
I think @Fenton explains it very well but I would like to add some more documentation and possible solutions.
Solutions:
Function overload. I prefer this solution in this case because it keeps some kind of type safety and avoids ignore and any. The original method and function call does not need to be rewritten at all.
function foo(...args: number[]): void
function foo(x: number, y: number, z: number) {
console.log(x, y, z);
}
var args: number[] = [0, 1, 2];
foo(...args);
Use @ts-ignore to ignore specific line, TypeScript 2.3
function foo(x: number, y: number, z: number) {
console.log(x, y, z);
}
var args: number[] = [0, 1, 2];
// @ts-ignore
foo(...args);
Use as any.
function foo(x: number, y: number, z: number) {
console.log(x, y, z);
}
var args: number[] = [0, 1, 2];
(foo as any)(...args);
Link with documentation regarding the spread operator:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html
Discussions regarding this:
https://github.com/Microsoft/TypeScript/issues/5296 https://github.com/Microsoft/TypeScript/issues/11780 https://github.com/Microsoft/TypeScript/issues/14981 https://github.com/Microsoft/TypeScript/issues/15375
Videos
Edit: This seems to be an issue since 2016, and apparently, no fix (yet? since 2016) because it seems like just an edge case.
Hi all, I accidentally mistyped [ with { at line 4 in the code below and it passes compiler check. Should this happen and why does it behave like that?
type Foo = number // just an example
let t: Foo[] = [] // [1,2,3]
let c: Foo[] = {...t}
console.log(c.map(e=>-e))It took me a few minutes in a sea of code to realise what's wrong. Needless to say, it was quite frustrating, I'm sorry if this is a stupid question.
playground link
here is my tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",So firstly, Object Rest/Spread is a proposed ECMAScript feature that is well on its way to being standardized, having reached Stage 4, and is in the process of being formally adopted.
As you know from its usage, it makes working with plain JavaScript objects, incredibly flexible.
Information about the typing of the feature is available in the TypeScript 2.1 documentation. As it very eloquently states:
Object rests are the dual of object spreads, in that they can extract any extra properties that don’t get picked up when destructuring an element:
And indeed there are actually two features in play, the one complementing the other.
Object Rest
When the Rest portion of the feature is used, it enhances object destructuring by enabling us to collect the rest of the properties into a new object comprised of them.
We can write the type annotation as we would for any other value. For example
interface GroupProperties {
name: string;
text: string;
isRequired?: boolean;
values: string[];
flagged: boolean;
}
function Group({ name, text, isRequired, ...rest }: GroupProperties) {
console.log(rest);
}
This informs the type system that name and text are of type string and that is required is of type boolean.
Further the type system then knows that rest has two properties, values and flagged of types boolean and string respectively. The type of rest is deduced.
Object Spread
When the Spread portion of the feature is used it enhances object construction by enabling declarative construction of an object from multiple sources, effortless creating derivatives, as well as easy undefining and overriding.
The type system also understands the meaning of Spread expressions and infers the types they evaluate to.
const o = {x: 1, y: 'hello'};
const o1 = {
...o,
y: 1
};
In the above, o1 has type {x: number, y: number}.
function GetGroup({ name, text, isRequired, ...props }: { name: string; text: string; isRequired: boolean; other: number; arg: string }) {
props.other // number
props.arg // string
}
TypeScript is just about adding types.. and name, text and isRequired are normal arguments. props, on the other hand, are the rest of the arguments. So, whatever the remaining arguments, are assumed to be the rest of the declared types.