Nikos
Nikos

Reputation: 724

Typescript Array[T] accepts {..T} as valid type

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

Answers (2)

georg
georg

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

PG

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

Andrei Tătar
Andrei Tătar

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

Related Questions