programowanieSenior TypeScript developer

Czym są Mapped Types w TypeScript i jak za ich pomocą tworzyć elastyczne typy generyczne? Proszę szczegółowo omówić niuanse ich użycia i potencjalne pułapki.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Mapped Types to typy, które są konstruowane dynamicznie poprzez przekształcanie (zmiana nazw, modyfikacja) wszystkich właściwości innego typu. Składnia opiera się na konstrukcji in:

type Readonly<T> = { readonly [K in keyof T]: T[K]; } type User = { name: string; age: number; } const u: Readonly<User> = { name: 'Eve', age: 22 }; u.name = 'Bob'; // Błąd: name jest tylko do odczytu

Niuanse:

  • Można zmieniać modyfikatory (readonly, optional), usuwać lub dodawać je za pomocą słów kluczowych -? lub +?.
  • Mogą być stosowane wewnętrznie, łączyć się z typami warunkowymi, typami pomocniczymi i generycznymi.
  • Nie zawsze błędy są łatwe do śledzenia na etapie rozwijania, szczególnie przy skomplikowanej, zagnieżdżonej modyfikacji hierarchii typów.

Przykład ze wszystkimi modyfikatorami:

type PartialMutable<T> = { -readonly [K in keyof T]?: T[K]; };

Pytanie z pułapką.

„Czy zastosowanie mapped type z modyfikatorem optional wpływa tylko na właściwości pierwszego poziomu czy również na zagnieżdżone obiekty?”

Odpowiedź: Nie, mapped type z optional ? wpływa TYLKO na właściwości pierwszego poziomu. Zagnieżdżone obiekty muszą być przekształcane osobno, często przy użyciu rekurencji lub dodatkowych mapped types.

Przykład:

type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; };

Przykłady rzeczywistych błędów z powodu niewiedzy na temat niuansów tej tematyki.


Historia

W jednym projekcie, dla zaoszczędzenia czasu, zastosowano standardowy Partial<T> dla głębokiego obiektu formularza. Jednakże, właściwości drugiego i trzeciego poziomu nie stały się opcjonalne, co doprowadziło do nieoczekiwanych błędów w czasie wykonywania przy braku zagnieżdżonych kluczy.


Historia

Była próba usunięcia właściwości readonly tylko w obiektach podrzędnych, zastosowując mapped type tylko na górnym poziomie:

type Mutable<T> = { -readonly [K in keyof T]: T[K] }

W rezultacie, właściwości typu { readonly foo: { readonly bar: number } } pozostały niezmienione w zagnieżdżeniu, co zmyliło zespół i utrudniło wsparcie.


Historia

W złożonym modelu danych zastosowano zagnieżdżone mapped types do przekroju kilku typów pomocniczych (np. Readonly & Partial). Z powodu niewłaściwej kolejności ich kompozycji pojawiły się nieoczekiwane konflikty zgodności typów, a kompilator zaczął wyświetlać mylące komunikaty o błędach.