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:
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"
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:
Wady:
Używają generyków: getProperty<T, K extends keyof T>(), typ key jest ściśle ograniczony kluczami struktury.
Zalety:
Wady: