programowanieProgramista Fullstack

Jak działa operator keyof w TypeScript, do czego jest używany i jak wpływa na projektowanie bezpiecznych funkcji i API?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Historia pytania

TypeScript od początku był projektowany z myślą o ścisłej typizacji obiektów i pisaniu bardziej przewidywalnego kodu w JavaScript. Jednym z narzędzi, które zapewniają bezpieczeństwo przy dostępie do właściwości obiektów, jest operator keyof, który pojawił się w TypeScript 2.1. Pozwala on na tworzenie typów, reprezentujących zestaw kluczy (łańcuchów i symboli) dostępnych w zadeklarowanym typie obiektu.

Problem

W czystym JavaScript właściwości obiektu mogą być odwoływane przy użyciu dowolnego ciągu, a jeśli popełnisz błąd, otrzymasz undefined lub błąd, który zostanie wykryty dopiero w czasie wykonywania. Bez ścisłej typizacji łatwo popełnić literówkę lub zapomnieć o aktualizacji ciągu klucza podczas refaktoryzacji. Ponadto często wymagana jest funkcja, która przyjmuje tylko poprawne klucze konkretnego obiektu lub typu.

Rozwiązanie

Operator keyof tworzy typ unii ze wszystkich kluczy typu. Dzięki niemu można ograniczać dozwolone klucze, zwiększać bezpieczeństwo API (na przykład dla funkcji getter/setter) oraz tworzyć uniwersalne typy pomocnicze.

Przykład kodu:

type User = { name: string; age: number; active: boolean }; type UserKey = keyof User; // "name" | "age" | "active" function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { name: 'Tom', age: 33, active: true }; const age = getProp(user, 'age'); // wiek typu number

Kluczowe cechy:

  • Pozwala na deklarowanie typów "kluczy" zależnych od struktury obiektu.
  • Uczynia funkcje typu get/set właściwości maksymalnie bezpiecznymi typowo — wyklucza literówki oraz nieistniejące klucze.
  • Działa także z właściwościami symbolowymi (symbol), począwszy od TypeScript 2.7 i wyżej.

Pytania z pułapką.

Co zwraca keyof dla tablicy i czy można uzyskać indeksy?

keyof dla tablicy zwraca typy kluczy obiektu, którymi tablica w rzeczywistości jest w JS. Zwykle są to ciągi indeksów i specjalne właściwości tablicy.

Przykład kodu:

type A = keyof number[]; // "length" | "toString" | "pop" | ... | number

Czy keyof może zwrócić klucze dla typów unii?

Tak, keyof dla typu unii zwraca przecięcie kluczy obu obiektów, a nie ich złączenie.

Przykład kodu:

type A = {a: string, b: number } type B = {b: number, c: boolean } type C = keyof (A | B); // "b"

Co się stanie, jeśli właściwość obiektu jest opcjonalna? Czy keyof obsługuje "?"?

Tak, właściwości opcjonalne również wchodzą w skład wynikowego typu kluczy, ale nie oznacza to, że właściwość jest obowiązkowo obecna w obiekcie w czasie wykonywania.

Przykład kodu:

type D = { a: string; b?: number }; type DK = keyof D; // "a" | "b"

Typowe błędy i antywzorce

  • Używać stałych łańcuchów dla kluczy zamiast keyof — utrata typizacji.
  • Sądzić, że keyof dla unii zwraca złączenie, a nie przecięcie.
  • Oczekiwać, że keyof uwzględnia tylko jawnie zadeklarowane klucze, a nie dziedziczone.

Przykład z życia

Negatywny przypadek

Piszą funkcję getProperty(obj, key) z kluczem typu łańcucha, wywołują ją z nieistniejącym kluczem — błąd pojawia się dopiero w czasie wykonywania.

Zalety:

  • Uniwersalny kod.

Wady:

  • Brak weryfikacji na etapie kompilacji, możliwe błędy podczas refaktoryzacji, podatność na literówki.

Pozytywny przypadek

Używają generyków: getProperty<T, K extends keyof T>(), typ key jest ściśle ograniczony kluczami struktury.

Zalety:

  • Absolutne bezpieczeństwo typowe, błędy literówek lub błędy w kluczu są niemożliwe.

Wady:

  • Kod jest nieco bardziej skomplikowany, generyki nie zawsze są zrozumiałe dla nowicjuszy.