Menu

Record w TypeScript: typ Record<K, V> na przykładach

Record<K, V> to typ obiektu, którego klucze są typu K, a wszystkie wartości typu V. Zobacz Record z kluczami string i z kluczami z unii (każdy klucz wymagany), Partial<Record>, iterowanie po Record z otypowanymi kluczami, pułapkę brakującego klucza i kiedy zamiast tego użyć sygnatury indeksu lub Map.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Record<K, V> to wbudowany typ narzędziowy dla obiektu, którego klucze mają typ K, a wszystkie wartości typ V. Z kluczami string opisuje słownik; z unią kluczy literałowych opisuje obiekt, który musi mieć dokładnie te klucze.

Record istnieje tylko w systemie typów. W czasie działania oba obiekty to zwykłe obiekty JavaScript, więc działają z literałami obiektów, rozkładaniem, JSON.stringify i wszystkim innym, co przyjmuje obiekt.

Składnia i definicja

Record<Keys, Value>

Keys musi być czymś, co może być kluczem obiektu: string, number, symbol, unią literałów tekstowych lub liczbowych albo template literal type. Value może być dowolnym typem. Cała definicja w bibliotece standardowej TypeScriptu to jedna linia, typ mapowany:

type Record<K extends keyof any, T> = {
  [P in K]: T;
};

keyof any to string | number | symbol, zbiór wszystkich możliwych typów kluczy. [P in K]: T tworzy jedną właściwość typu T dla każdego elementu K. To wyjaśnia oba zachowania opisane niżej: szeroki K, taki jak string, daje sygnaturę indeksu (dowolny klucz), a K będący unią daje jedną wymaganą właściwość na każdy element.

Klucze z unii: każdy klucz jest wymagany

Gdy klucze są unią literałów, Record musi zawierać każdy z nich i żadnego innego. To zamienia kompilator w listę kontrolną:

Dodaj "cancelled" do Status, a oba obiekty przestaną się kompilować, dopóki nie dasz nowemu statusowi etykiety i koloru. Pominięcie klucza albo dodanie takiego, którego nie ma w unii, to błąd kompilacji:

index.ts(4,7): error TS2741: Property 'error' is missing in type '{ idle: string; loading: string; success: string; }' but required in type 'Record<Status, string>'.
index.ts(11,54): error TS2353: Object literal may only specify known properties, and 'paused' does not exist in type 'Record<Status, string>'.

To samo działa z enumem tekstowym jako typem klucza: Record<Color, string> wymaga jednego wpisu na każdy element enuma.

Record<string, T> i brakujący klucz

Przy kluczach string dozwolony jest dowolny klucz, a TypeScript typuje każdy odczyt jako T, nawet dla klucza, którego nie ma. W czasie działania brakujący klucz daje undefined:

To najczęstszy błąd z Record. Są trzy sposoby, żeby sobie z nim poradzić: sprawdź przez in lub Object.hasOwn przed odczytem, zadeklaruj wartość jako V | undefined albo włącz opcję kompilatora noUncheckedIndexedAccess, która dodaje | undefined do każdego odczytu przez sygnaturę indeksu w projekcie. Record z kluczami z unii nie ma tego problemu, bo każdy klucz na pewno istnieje.

Partial<Record<K, V>>: tylko niektóre klucze

Aby użyć unii kluczy, ale nie wymagać wszystkich, opakuj Record w Partial. Odczyty zwracają wtedy V | undefined, co jest uczciwe:

Błędnie wpisany klucz, taki jak jp, nadal jest błędem, i to przewaga nad Record<string, string>.

Iterowanie po Record

Object.keys, Object.values i Object.entries działają. Haczyk tkwi w typie klucza: Object.keys zwraca string[], a Object.entries zwraca [string, V][], nigdy twoją unię kluczy:

TypeScript celowo zostawia klucze jako string: obiekt może mieć w czasie działania więcej właściwości, niż wymienia jego typ, więc obietnica Plan[] byłaby ogólnie niebezpieczna. Dla obiektu utworzonego z literału, jak seats, rzutowanie jest bezpieczne.

Budowanie Record z danych

Record to typowy wynik grupowania lub indeksowania tablicy. Zacznij od pustego obiektu o typie Record i wypełnij go:

Book["genre"] używa unii z interfejsu jako typu klucza, więc dodanie gatunku do Book sprawia, że byGenre wymaga nowego wpisu.

Record<string, unknown> i interfejsy

Record<string, unknown> to popularny typ dla "jakiegoś obiektu z kluczami tekstowymi". Przyjmuje literały obiektów i wartości otypowane aliasem type, ale interfejs zostaje odrzucony:

index.ts(11,11): error TS2345: Argument of type 'User' is not assignable to parameter of type 'Record<string, unknown>'.
  Index signature for type 'string' is missing in type 'User'.

Interfejsy można rozszerzać przez łączenie deklaracji, więc TypeScript nie zakłada, że pasują do sygnatury indeksu; aliasów typów nie można ponownie otworzyć, więc type User = { name: string } by przeszedł. Typowe rozwiązania to przyjmować zamiast tego object (nadal możesz wywołać na nim Object.keys), uczynić funkcję generyczną (<T extends object>(obj: T)) albo zadeklarować User przez type (strona interface vs type omawia pozostałe różnice).

Record a sygnatura indeksu a Map

Record<K, V>{ [key: string]: V }Map<K, V>
Istnieje w czasie działanianie, zwykły obiektnie, zwykły obiekttak, klasa
Stały zbiór kluczytak, z unią Knienie
Typy kluczystring, number, symbol, unie literałów, wzorce templatestring, number, symbol, wzorce templatecokolwiek, także obiekty
Typ odczytu brakującego kluczaV (z kluczami string)VV | undefined z get
Łączenie z nazwanymi właściwościamiprzez przecięcie &tak, w tym samym typienie
JSON i rozkładanietaktaknie, najpierw konwersja
RozmiarObject.keys(r).lengthObject.keys(o).lengthm.size
Częste dodawanie i usuwaniedziaładziałazaprojektowana do tego

Wybieraj Record z kluczami z unii zawsze, gdy zbiór kluczy jest znany: to jedyna opcja, która sprawdza obecność każdego klucza. Dla otwartych kluczy tekstowych Record<string, V> i sygnatura indeksu są wymienne, a wiele projektów woli Record dla czytelności. Sięgnij po Map, gdy klucze są dodawane i usuwane w czasie działania, gdy klucze nie są stringami albo gdy potrzebujesz rozmiaru i kolejności wstawiania bez dodatkowej pracy.

Najczęściej zadawane pytania

Czym jest Record w TypeScript?

Record<K, V> to wbudowany typ narzędziowy dla obiektu, którego klucze są typu K, a wszystkie wartości typu V. Record<string, number> to obiekt z dowolnymi kluczami tekstowymi i wartościami liczbowymi; Record<"en" | "de", string> to obiekt z dokładnie kluczami en i de, oba typu string.

Czym różni się Record od Map w TypeScript?

Record to typ dla zwykłego obiektu JavaScript, więc działa z literałami obiektów, JSON-em i rozkładaniem, a znika podczas kompilacji. Map to klasa istniejąca w czasie działania, z get, set, has i size, zachowuje kolejność wstawiania wszystkich kluczy, przyjmuje klucze dowolnego typu (także obiekty), a get zwraca V | undefined. Używaj Record dla danych stałych lub w kształcie JSON-a, a Map dla kluczy dodawanych i usuwanych w czasie działania.

Czym różni się Record<string, T> od { [key: string]: T }?

Dla wartości to ten sam typ: Record<string, T> rozwija się do typu obiektowego z sygnaturą indeksu string. Dwie drobne różnice: sygnatura indeksu może mieć nazwę i stać obok innych właściwości w tym samym typie, a keyof Record<string, T> to string, podczas gdy keyof { [key: string]: T } to string | number.

Jak iterować po Record w TypeScript?

Użyj Object.entries(record) dla par klucz-wartość, Object.keys dla kluczy i Object.values dla wartości. Klucze wracają jako string, a nie K, bo obiekt może mieć w czasie działania dodatkowe klucze. Gdy Record ma unię znanych kluczy, rzutuj: (Object.keys(r) as Array<keyof typeof r>).

Jak sprawić, by tylko niektóre klucze Record były wymagane?

Przy kluczach z unii Record<K, V> wymaga każdego klucza. Opakuj go w Partial, żeby wszystkie były opcjonalne: Partial<Record<Lang, string>>. Dla mieszanki użyj przecięcia: Record<"en", string> & Partial<Record<"de" | "fr", string>> wymaga en i dopuszcza pozostałe.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ