Menu

Partial i Required w TypeScript: przykłady i Deep Partial

Partial<T> czyni każdą właściwość T opcjonalną, a to dokładnie typ obiektu aktualizacji lub łatki. Zobacz Partial w funkcjach aktualizujących, dlaczego jest płytki, jak napisać DeepPartial, pułapkę jawnego undefined oraz jego przeciwieństwo, Required<T>.

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

Partial<T> to wbudowany typ narzędziowy, który czyni każdą właściwość T opcjonalną. To naturalny typ dla aktualizacji lub łatki: wywołujący wysyła tylko pola, które się zmieniły.

Partial<User> to { id?: number; name?: string; email?: string }. Kompilator nadal sprawdza pola, które przekazujesz: { nmae: "x" } czy { name: 42 } to błąd, i właśnie dlatego Partial jest lepszy niż luźny parametr object lub any.

Jak zdefiniowany jest Partial

Partial to jednolinijkowy typ mapowany w bibliotece standardowej TypeScriptu:

type Partial<T> = {
  [P in keyof T]?: T[P];
};

Dla każdego klucza P z T deklaruje opcjonalną właściwość o tym samym typie. Ponieważ mapuje po keyof T, zachowuje readonly na właściwościach, które je miały. Jako zwykły typ nie ma wpływu na działanie programu: obiekt przekazany jako changes jest w obu przypadkach tym samym obiektem.

Odczyt z Partial daje T | undefined

Każdej właściwości Partial<T> może brakować, więc jej odczyt daje typ właściwości plus undefined. Kompilator każe ci obsłużyć przypadek braku:

{ ...defaults, ...opts } to typowy sposób, żeby zamienić Partial<Options> z powrotem w kompletne Options: najpierw rozłóż wartości domyślne, a podane wartości niech je nadpiszą.

Pułapka jawnego undefined

Właściwości opcjonalnej może brakować, ale może też istnieć z wartością undefined. Rozkładanie obiektu kopiuje to undefined na prawdziwą wartość, a typ wyniku tego nie pokazuje:

To gryzie, gdy łatka powstaje z formularza lub query stringa, w którym puste pola stają się undefined. Opcja kompilatora exactOptionalPropertyTypes (nie jest częścią strict) sprawia, że { name: undefined } to błąd kompilacji dla name?: string, chyba że napiszesz name?: string | undefined, co zapobiega problemowi u źródła.

Partial jest płytki

Partial czyni opcjonalnymi tylko właściwości najwyższego poziomu. Zagnieżdżony obiekt, jeśli go przekazujesz, musi być kompletny:

index.ts(8,3): error TS2741: Property 'tabSize' is missing in type '{ fontSize: number; }' but required in type '{ fontSize: number; tabSize: number; }'.

Właściwość editor jest opcjonalna, ale gdy już jest, ma typ { fontSize: number; tabSize: number }, bez zmian. Przy obiektach ustawień, łatkach API i danych testowych często chcesz właściwości opcjonalnych na każdym poziomie. To wymaga typu rekurencyjnego.

DeepPartial: rekurencyjny Partial

TypeScript nie ma wbudowanej głębokiej wersji, ale to kilka linii. Funkcje i tablice zostają bez zmian, bo opcjonalne elementy tablicy pozwoliłyby na [undefined]:

Typ jest rekurencyjny, scalanie już nie. applySettings scala editor ręcznie, bo rozkładanie obiektu też jest płytkie. Generyczna funkcja głębokiego scalania istnieje w bibliotekach takich jak lodash (merge), a jej typowanie jest trudniejsze niż powyższy typ.

Required: przeciwieństwo Partial

Required<T> usuwa ? z każdej właściwości. Jest zdefiniowany z modyfikatorem -?, który usuwa też undefined z typu każdej właściwości:

Wzorzec jest taki sam jak przy Partial, tylko odwrócony: użytkownicy API przekazują luźną konfigurację, a kod w środku pracuje na wersji Required, w której każda wartość na pewno istnieje. Rozkładanie ma tę samą dziurę co funkcja aktualizująca wyżej: wywołujący, który jawnie przekaże port: undefined, nadpisze wartość domyślną przez undefined, a kompilator to zaakceptuje, chyba że exactOptionalPropertyTypes jest włączone. Required jest płytki tak samo jak Partial.

Tylko niektóre właściwości opcjonalne lub wymagane

Partial i Required działają na każdą właściwość. Aby zmienić tylko kilka, podziel typ przez Pick i Omit i złóż go z powrotem:

type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;

interface Post {
  id: number;
  title: string;
  body?: string;
}

type NewPost = PartialBy<Post, "id">;      // id optional, title required, body optional
type Published = RequiredBy<Post, "body">; // body now required
TypEfektGłęboko?
Partial<T>każda właściwość opcjonalnanie
Required<T>każda właściwość wymagana, undefined usuniętenie
DeepPartial<T> (własny)opcjonalne na każdym poziomietak
PartialBy<T, K> (własny)opcjonalne tylko klucze Knie
Readonly<T>każda właściwość readonlynie

Najczęściej zadawane pytania

Co robi Partial w TypeScript?

Partial<T> tworzy typ ze wszystkimi właściwościami T oznaczonymi jako opcjonalne. Dla interface User { name: string; email: string } Partial<User> to { name?: string; email?: string }, więc {}, { name: "Ada" } i pełny użytkownik to poprawne wartości.

Czy Partial w TypeScript działa głęboko?

Nie, Partial dotyczy tylko właściwości najwyższego poziomu. Zagnieżdżony obiekt wewnątrz Partial<T> nadal musi być kompletny. Dla wersji rekurencyjnej napisz typ DeepPartial<T>, który stosuje się sam do siebie na właściwościach o typie obiektowym.

Co jest przeciwieństwem Partial w TypeScript?

Required<T>. Usuwa ? z każdej właściwości i usuwa też undefined z ich typów, więc Required<{ port?: number }> to { port: number }. Jest zdefiniowany jako typ mapowany z modyfikatorem -?.

Jak uczynić opcjonalnymi tylko niektóre właściwości?

Połącz Omit, Pick i Partial: type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>. PartialBy<User, "email"> zostawia każdą właściwość bez zmian poza email, które staje się opcjonalne.

Dlaczego właściwość jest undefined po scaleniu aktualizacji Partial?

Partial<T> pozwala, by właściwość istniała z wartością undefined, a rozkładanie obiektu ją kopiuje: { ...user, ...{ name: undefined } } ma name: undefined, choć TypeScript typuje wynik jako User. Odfiltruj wartości undefined przed scaleniem albo włącz exactOptionalPropertyTypes, żeby jawne undefined było odrzucane.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ