Reputation: 724
This bit me when using useState<Options[]>
as a react hook, when I mistyped {...options}
instead of [...options]
, I only found out when I tried to access .map
of "array" (object) and got a TypeError. Let me give you an example that can clarify my question:
interface Option {
title: string;
isSelected?: boolean;
}
function myFunc(options: Option[]): void {}
const options: Option[] = [{title: 'Male', isSelected: true}, {title: 'Female'}, {title: 'Other'}];
const options2 = {...options};
myFunc(options);
myFunc(options2); // why isn't this an error ?
Upvotes: 3
Views: 148
Reputation: 215009
This appears to be indeed a problem with Typescript itself:
let c: number[] = { ...[0, 1, 2] }; // compiles
c.fill(0) // runtime error
let d: number[] = Object.assign({}, [0, 1, 2]); // compiles
d.fill(0) // runtime error
Moreover, this also compiles and breaks at run-time:
class E {
method() { }
}
let e: E = Object.assign({}, new E)
e.method() // runtime error
I guess this is because Object.assign
is declared as
assign<T, U>(target: T, source: U): T & U;
which is incorrect, since what assign
returns doesn't actually extend U
. The return type of assign
should be something like T & OwnProperties<U>
, but this is currently not possible, see
Upvotes: 2
Reputation: 8295
Playing around on the typescript playground, it seems typescript thinks options2
is defined as:
const options2: {
[x: number]: Option;
length: number;
toString(): string;
toLocaleString(): string;
pop(): Option | undefined;
push(...items: Option[]): number;
concat(...items: ConcatArray<Option>[]): Option[];
concat(...items: (Option | ConcatArray<...>)[]): Option[];
... 24 more ...;
[Symbol.unscopables](): {
...;
};
}
So, it actually looks like an array of type Option Option[]
. But the spread operator doesn't actually copy the Array prototype members and the new object is definitely not an array. I would say this is a bug in the typescript type system.
Upvotes: 2