Historia pytania:
Spread (...) i Rest (...) to jedne z najpopularniejszych nowoczesnych konstrukcji składni, które pojawiły się w ES6. W TypeScript zyskały ścisłą typizację, dlatego użycie Spread i Rest stało się nie tylko wygodne, ale także bezpieczne, pod warunkiem przestrzegania ograniczeń typowych.
Problem:
Operacje Spread i Rest stają się źródłem błędów, jeśli nie uwzględniono typu wartości lub mieszane są niekompatybilne typy (na przykład, gdy łączone są obiekty z niekompatybilnymi właściwościami lub tablice różnych typów).
Rozwiązanie:
W TypeScript Spread służy do kopiowania lub łączenia właściwości obiektów i elementów tablic. Rest pozwala na zbieranie "pozostałych" elementów w funkcji lub tablicy, a ścisła typizacja sprawdza niekompatybilne przypadki, zapobiegając błędom na etapie kompilacji.
Przykład kodu:
// Spread z obiektami const base = { a: 1, b: 2 }; const extended = { ...base, c: 3 }; // extended: { a: number; b: number; c: number } // Rest w parametrach funkcji function sum(...args: number[]): number { return args.reduce((acc, val) => acc + val, 0); } // Spread w tablicach const arr = [1, 2, 3]; const newArr = [...arr, 4, 5];
Kluczowe cechy:
Czym różni się kopiowanie obiektu przez Spread od przypisania przez referencję?
Spread tworzy nowy obiekt z kopią wszystkich wyliczalnych właściwości, ale jeśli w obiekcie są zagnieżdżone obiekty, są one kopiowane przez referencję (shallow copy).
const base = { a: 1, nested: { x: 2 } }; const copy = { ...base }; copy.nested.x = 42; console.log(base.nested.x); // 42
Czy można za pomocą Spread kopiować tylko określone właściwości?
Nie, Spread kopiuje wszystkie wyliczalne właściwości obiektu. Do wybrania poszczególnych używa się destrukturyzacji:
const { a, ...rest } = { a: 1, b: 2, c: 3 }; // rest: { b: 2, c: 3 }
Co się stanie, jeśli połączymy przez Spread dwa obiekty z tymi samymi właściwościami? Jak to wpływa na typizację?
Właściwości z ostatniego obiektu nadpiszą właściwości poprzednich. Przy tym typ ostatniej właściwości będzie uznawany za ostateczny:
const a = { val: 1 }; const b = { val: 'hello' }; const merged = { ...a, ...b }; // merged: { val: string } (a nie number)
W projekcie postanowiono połączyć parametry konfiguracyjne przez Spread. Jeden z obiektów miał właściwość timeout: number, a drugi — timeout: string. Żadne błędy nie zostały zauważone aż do runtime'u, kiedy funkcja nie działała z powodu niepoprawnego typu.
Zalety:
Wady:
Użyto typizowanego Spread do łączenia ścisłych interfejsów, a także destrukturyzacji do oddzielenia zbędnych pól. Błędy wyłapał od razu kompilator.
Zalety:
Wady: