Typescript Array[T] akzeptiert {..T} als gültigen Typ
Das hat mich erwischt, als ich useState<Options[]> als React-Hook verwendet habe. Als ich {...options} statt [...options] falsch geschrieben habe, habe ich es erst bemerkt, als ich versucht habe, auf .map eines "Arrays" (Objekts) zuzugreifen und einen TypeError bekam. Lassen Sie mich ein Beispiel geben, das meine Frage klären kann:
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 ?
Antwort #1
Dies scheint tatsächlich ein Problem mit TypeScript selbst zu sein:
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
Außerdem kompiliert dies ebenfalls und bricht zur Laufzeit ab:
class E {
method() { }
}
let e: E = Object.assign({}, new E)
e.method() // runtime error
PG
Ich vermute, dass dies daran liegt, dass Object.assign deklariert ist als
assign<T, U>(target: T, source: U): T & U;
was falsch ist, da das, was assign zurückgibt, U tatsächlich nicht erweitert. Der Rückgabewert von assign sollte so etwas wie T & OwnProperties<U> sein, aber das ist derzeit nicht möglich, siehe
- https://github.com/microsoft/TypeScript/issues/28801
- https://github.com/microsoft/TypeScript/issues/9726
Antwort #2
Wenn man im TypeScript Playground herumspielt, scheint TypeScript zu denken, dass options2 wie folgt definiert ist:
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](): {
...;
};
}
Also sieht es tatsächlich aus wie ein Array vom Typ Option Option[]. Aber der Spread-Operator kopiert die Array-Prototyp-Mitglieder nicht wirklich und das neue Objekt ist definitiv kein Array. Ich würde sagen, das ist ein Fehler im TypeScript-Typensystem.
