programowanieArchitekt TypeScript

Jak działają typy warunkowe w TypeScript? Czym jest dystrybucyjne zachowanie typów warunkowych i jak z nim pracować? Wyjaśnij na przykładach.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Typy warunkowe w TypeScript pozwalają opisać jeden typ za pomocą innego na zasadzie T extends U ? X : Y. Takie typy dają potężne możliwości przy budowaniu złożonej logiki typów, szczególnie w bibliotekach i deklaracjach API.

Przykład podstawowego typu warunkowego:

type IsString<T> = T extends string ? 'tak' : 'nie'; type A = IsString<string>; // 'tak' type B = IsString<number>; // 'nie'

Dystrybucyjne zachowanie

Gdy typ warunkowy jest stosowany do typu unii, TypeScript "rozprowadza" warunek na każdy element unii osobno. Nazywa się to dystrybucyjnym zachowaniem typów warunkowych.

Przykład:

type Foo<T> = T extends { id: number } ? string : boolean; type Result = Foo<{id: number} | {name: string}>; // string | boolean

Jest to bardzo potężna funkcja, ale może powodować zamieszanie z oczekiwanym wynikiem, zwłaszcza podczas pracy z tablicami i mapowaniem typów.

Jak uniknąć dystrybucyjnego zachowania

Owijamy typ w krotkę:

type NoDistrib<T> = [T] extends [{id: number}] ? string : boolean; type Result = NoDistrib<{id: number} | {name: string}>; // boolean

Pytanie z podstępem.

Pytanie: "Co się stanie, jeśli użyjemy typu warunkowego z typami unii, nie owiniętym w krotki? Czy wynik zawsze będzie taki sam jak w przypadku zwykłej logiki?"

Odpowiedź: Wynik może być nieprzewidywalny! Z powodu dystrybucji warunek jest stosowany do każdego człona typów unii osobno. Aby dokładnie porównać cały typ unii, należy użyć opakowania (tuple).

Przykład:

type Test<T> = T extends string ? number : boolean; type A = Test<string | boolean>; // number | boolean, a nie tylko boolean

Przykłady rzeczywistych błędów z powodu nieznajomości niuansów tematu.


Historia

W bibliotece do serializacji użyto typu warunkowego do sprawdzania struktury danych, ale zapomniano owinąć parametr generyczny w krotkę. W rezultacie na skomplikowanych typach unii deklaracje łamały się, a kompilator zwracał nieprzewidywalne typy podczas używania API.


Historia

Podczas próby realizacji transformacji typów do przetwarzania pól modeli wystąpiła utrata części informacji: z powodu tego, że dystrybucyjność generuje unie, kilka gałęzi logiki zapomniano przetworzyć, i ostatecznie typizacja stała się zbyt permissywna.


Historia

Programista uważał, że T extends SomeType wewnątrz typów warunkowych zachowa się tak, jak oczekujemy dla całego obiektu, ale nastąpiło "rozproszenie" — kompilator wskazał na niespójność, zaczęły się zamieszanie typów i poważne błędy podczas generowania dokumentacji automatycznej.