Typy przecięcia (Intersection Types) w TypeScript pozwalają na tworzenie typów złożonych, które łączą w sobie właściwości i metody wszystkich typów podstawowych. To potężne narzędzie do budowania elastycznych i rozszerzalnych struktur danych bez nadmiarowego dziedziczenia klas. Konstrukcja realizowana jest za pomocą operatora & między typami.
TypeScript od pierwszych wersji wspiera typy unijne (|) do wyrażania „lub”, ale często pojawia się zadanie opisania obiektu z wieloma właściwościami z różnych niezależnych interfejsów lub typów. Wtedy wykorzystuje się przecięcie (&) — obiekt musi spełniać wszystkie interfejsy w odniesieniu do wszystkich właściwości.
Jednym z głównych problemów jest konflikt tych samych nazw właściwości w typach przeciętych, różnice w ich typizacji oraz poprawność finalnego złożonego typu, gdy łączą się klasy z prywatnymi lub chronionymi polami. Często myli się przecięcie typów z unią (union types), co prowadzi do nieoczywistych błędów kompilacji lub pracy z obiektem.
Przecięcie typów agreguje wszystkie właściwości z łączonych typów, a dla każdej właściwości wymagana jest zgodność obu typów, jeśli nazwa się pokrywa. Używane jest zarówno dla interfejsów, jak i dla aliasów typów.
interface A { foo: string; } interface B { bar: number; } type AB = A & B; const item: AB = { foo: "hello", bar: 123 }; // Poprawnie
Jeśli przecięte są właściwości o tej samej nazwie, typ musi być zgodny, w przeciwnym razie pojawia się błąd:
interface X { val: string; } interface Y { val: number; } // type Z = X & Y; // Błąd: niezgodność val
Kluczowe cechy:
& łączy wszystkie właściwości obu typów (interfejsów i aliasów typów).Co się stanie, jeśli typy się przecinają, a niektóre właściwości mają niezgodne typy? (Na przykład jedna właściwość to string, a inna number)
Pojawi się błąd kompilacji, ponieważ właściwość nie może być jednocześnie zarówno string oraz number.
Czy można przeciąć klasy z prywatnymi lub chronionymi właściwościami?
Można, ale jeśli takie pola mają te same nazwy i różne typy dostępu, wynik będzie nieważny, a TypeScript zgłosi błąd.
Czym różni się typ przecięcia od typu unijnego (|)?
Przecięcie (A & B) wymaga, aby obiekt był jednocześnie obu typów, natomiast unia (A | B) — aby był przynajmniej jednym z nich. Na przykład:
type U = A | B; // Można foo lub bar lub oba type I = A & B; // Obowiązkowo oba właściwości
Programista tworzy typ przez przecięcie kilku nieskonsolidowanych interfejsów; właściwości konfliktują, pojawiają się trudne do debugowania błędy typizacji.
Zalety: Możliwość szybkiego połączenia możliwości kilku typów
Wady: Kod się nie kompiluje lub zawiera niejawne błędy, debugowanie jest trudne
Podział funkcji na niezależne interfejsy i staranne łączenie ich przez typ przecięcia w celu utworzenia finalnych obiektów złożonych.
Zalety: Skalowalność, prostota testowania i rozszerzania, ścisły kontrakt
Wady: Dodatkowa praca nad projektowaniem interfejsów i ich uzgadnianiem