Przejdź do treści
webtype.orgwebtype.org

    ↑↓ ruch · ⏎ otwórz · esc zamknij

    Zagadnienie 12 z 12

    Dokładna równość

    Każdy test na tej stronie sprawdza dokładną tożsamość typów, a nie przypisywalność. Ten wybór sprawia, że odpowiedź może być *użyteczna* i mimo to nie przejść — a zrozumienie tego odróżnia walkę ze sprawdzarką od jej czytania.

    Prześledź typSprawdzone kompilatorem
    WyrażenieA extends B ? true : false
    Rozwija się dotrue
    WyrażenieB extends A ? true : false
    Rozwija się dotrue
    WyrażenieEqual<A, B>
    Rozwija się dofalse

    Przypisywalność w obie strony nadal nie oznacza równości w tym teście. Przecięcie zachowuje inną reprezentację.

    Definicja typu
    type A = { a: string } & { b: number }
    type B = { a: string; b: number }
    type Equal<X, Y> =
      (<T>() => T extends X ? 1 : 2) extends
      (<T>() => T extends Y ? 1 : 2) ? true : false

    Dlaczego extends nie wystarcza

    type Inter = { a: string } & { b: number }
    type Flat = { a: string; b: number }
    
    type OneWay = Inter extends Flat ? true : false   // true
    type Other = Flat extends Inter ? true : false    // true
    // Mutually assignable — yet they are not the same type.

    Wzajemna przypisywalność jest słabsza niż tożsamość. Przecięcie i spłaszczony obiekt przyjmują się nawzajem, ale różnią się strukturalnie — a sprawdzarka oparta na extends uznałaby niedokończoną odpowiedź za poprawną.

    Sztuczka z Equal

    type Equal<X, Y> =
      (<T>() => T extends X ? 1 : 2) extends
      (<T>() => T extends Y ? 1 : 2) ? true : false

    Dwa odroczone warunki są wzajemnie przypisywalne tylko wtedy, gdy kompilator rozwiązuje je identycznie — a robi to wyłącznie dla naprawdę identycznych typów. To wewnętrzny szczegół implementacji, a nie udokumentowana funkcja, i najbliższa rzecz do równości typów, jaką TypeScript udostępnia.

    Spłaszczanie przecięcia

    type Prettify<T> = { [K in keyof T]: T[K] } & {}
    
    type Fixed = Prettify<{ a: string } & { b: number }>
    // { a: string; b: number } — now Equal passes

    Mapowanie po zebranych kluczach tworzy świeży typ obiektowy. Końcowe & {} zmusza TypeScript do faktycznego wyliczenia mapowania zamiast trzymania go leniwie — dlatego ten idiom wygląda jak literówka, a nią nie jest.

    Najczęstsza błędna odpowiedź

    // Intent: does this tuple contain exactly `true`?
    type Includes<T extends unknown[], U> = U extends T[number] ? true : false
    
    Includes<[boolean], true>   // true — but the tuple holds boolean, not true

    extends pyta, czy true mieści się w boolean — i mieści. Przynależność to nie przypisywalność, a gdy zadanie mówi „dokładnie”, extends jest złym narzędziem.

    Ćwiczenie

    Odpowiedz true tylko wtedy, gdy obie strony to ten sam typ — przecięcie nie jest płaskim obiektem.

    Spróbuj

    1
    • Flatten<{ a: 1 } & { b: 2 }>—
      → { a: 1; b: 2 }

    Do zapamiętania

    Przypisywalne to nie równe. Gdy test odrzuca odpowiedź, która wyraźnie działa, winowajcą jest zwykle przecięcie, którego nikt nie spłaszczył.