Typy narzędziowe i satisfies
Profil pełny, formularz zmiany i publiczna wizytówka mają wspólne dane. Zbuduj powiązane typy bez kopiowania całego interfejsu.
Po tej lekcji potrafisz
- Zastosujesz Partial, Required, Pick, Omit, Record i Readonly.
- Odczytasz ReturnType, Parameters i Awaited w małych przykładach.
- Rozróżnisz sprawdzanie przez satisfies od asercji as.
Zmiana profilu nie jest pełnym profilem
Do zmiany nicku nie potrzebujesz ponownie wysyłać id i wszystkich statystyk. Zamiast ręcznie tworzyć prawie taki sam interfejs, wyprowadź typ aktualizacji z modelu. Zmiana modelu będzie wtedy widoczna w typach zależnych.
Małe kroki, konkretne efekty
Wybieraj pola i zmieniaj ich obowiązkowość
Pick wybiera pola, Omit je pomija, Partial czyni je opcjonalnymi, a Required usuwa opcjonalność. Działają na poziomie typów — nie usuwają pól z istniejącego obiektu ani nie dodają danych.
interface Profil { id: number; nick: string; opis?: string }
type Wizytowka = Pick<Profil, "nick" | "opis">;
type Zmiana = Partial<Omit<Profil, "id">>;
const karta: Wizytowka = { nick: "Pixel" };
const zmiana: Zmiana = { opis: "Lubię gry" };
const pelny: Required<Profil> = { id: 1, nick: "Ala", opis: "" };
console.log(karta.nick, zmiana.opis, pelny.id);
Pixel Lubię gry 1
Partial nie działa automatycznie głęboko. Jeżeli pole adres istnieje i ma wymagane miasto, Partial<ProfilZAdresem> pozwoli pominąć adres, ale nie przerobi jego wnętrza.
Record i Readonly
Record<K, V> opisuje wartości V pod kluczami K. Gdy K jest zamkniętą unią, musisz dostarczyć wszystkie jej klucze. Readonly zabrania przypisań do pól przez ten typ.
type Rola = "gracz" | "moderator";
const limity: Readonly<Record<Rola, number>> = { gracz: 3, moderator: 10 };
console.log(limity.gracz, limity.moderator);
3 10
Record<string, number> nie oznacza, że w runtime istnieje każdy możliwy tekstowy klucz. Przy otwartym słowniku kontroluj brak wpisu albo wybierz Map. Readonly nie zamraża obiektu w JavaScripcie.
Aktualizacja profilu bez podmiany id
Funkcja przyjmuje tylko opcjonalne pola, które można edytować. Tworzymy wynik jawnie, aby dodatkowe właściwości obiektu wejściowego nie mogły nadpisać identyfikatora.
interface Profil { id: number; nick: string; opis: string }
type ZmianaProfilu = Partial<Pick<Profil, "nick" | "opis">>;
function aktualizuj(p: Profil, zmiana: ZmianaProfilu): Profil {
return { id: p.id, nick: zmiana.nick ?? p.nick, opis: zmiana.opis ?? p.opis };
}
const stary: Profil = { id: 1, nick: "Ala", opis: "" };
const nowy = aktualizuj(stary, { nick: "Pixel" });
console.log(stary.nick, nowy.nick, nowy.id);
const reguly = { gracz: 3, moderator: 10 } satisfies Record<"gracz" | "moderator", number>;
console.log(reguly.gracz);
Ala Pixel 1
3
Jak program dochodzi do wyniku
- Pick dopuszcza tylko nick oraz opis w typie zmiany.
- Partial pozwala podać jedno z tych pól lub żadne.
- Jawne składanie wyniku zachowuje id także przy obiekcie wejściowym posiadającym dodatkowe pola.
- satisfies sprawdza zgodność wyrażenia z wymaganym typem, zachowując informację o konkretnym wyrażeniu; nie jest rzutowaniem danych.
Zanim przejdziesz dalej
Czy Omit<Profil, „id”> fizycznie usuwa id z istniejącego obiektu?
Sprawdź swoje rozumowanie
Nie. To tylko opis typu. Do usunięcia pola w runtime trzeba zbudować nowy obiekt z wybranymi polami lub wykonać odpowiednią operację.
Zmień kod i zobacz, co się stanie
Typy wyprowadzone z funkcji
ReturnType odczytuje typ wyniku funkcji, Parameters tworzy krotkę parametrów, a Awaited opisuje wynik po oczekiwaniu na obietnicę. typeof w miejscu typu odczytuje typ istniejącej wartości.
function podsumuj(nick: string, punkty: number) { return { nick, punkty }; }
type Argumenty = Parameters<typeof podsumuj>;
type Raport = ReturnType<typeof podsumuj>;
type Liczba = Awaited<Promise<number>>;
const args: Argumenty = ["Ala", 12];
const raport: Raport = podsumuj(...args);
const n: Liczba = 5;
console.log(raport.nick, n);
const tryby = ["solo", "duet"] as const;
type Tryb = typeof tryby[number];
const tryb: Tryb = "duet";
console.log(tryb);
Ala 5
duet
as const zachowuje typy literalne i readonly w opisie tej wartości; nie wywołuje Object.freeze. Dodatkowe narzędzia do unii to Exclude (usuwa warianty), Extract (wybiera wspólne) i NonNullable (usuwa null oraz undefined). Nie walidują one danych w runtime.
Gdy wynik zaskakuje
| Pomyłka | Co się dzieje i jak to poprawić |
|---|---|
| Omit uznany za filtrowanie danych | Typ znika po kompilacji, więc pola nie są automatycznie usuwane. |
| Readonly jako głębokie zamrożenie | Chroni przypisania przez dany typ, zwykle płytko. |
| as zamiast satisfies do sprawdzenia konfiguracji | Asercja może ukryć niezgodność. satisfies ma ją wykryć. |
Teraz Twoja kolej
Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.
Z Uczen z polami id, imie, email wybierz tylko imie i email przez Pick. Utwórz wizytówkę.
Mała podpowiedź
Drugi argument Pick to unia kluczy.
Przykładowe rozwiązanie z wyjaśnieniem
interface Uczen { id: number; imie: string; email: string }
type Karta = Pick<Uczen, "imie" | "email">;
const karta: Karta = { imie: "Ala", email: "ala@example.com" };
console.log(karta.imie);
Ala
Publiczny model nie wymaga id.
Record ma zawierać klucze easy, normal i hard z liczbą przeciwników 2, 5, 10. Użyj satisfies i odczytaj hard.
Mała podpowiedź
Pominięcie któregoś klucza ma zostać wykryte.
Przykładowe rozwiązanie z wyjaśnieniem
const poziomy = { easy: 2, normal: 5, hard: 10 } satisfies Record<"easy" | "normal" | "hard", number>;
console.log(poziomy.hard);
10
Zamknięta unia kluczy wymaga kompletnej konfiguracji.
Ze Status = „nowe” | „gotowe” | „usuniete” usuń wariant usuniete przez Exclude. Przez Extract wybierz gotowe. Utwórz poprawne wartości obu typów.
Mała podpowiedź
Narzędzia działają na wariantach unii.
Przykładowe rozwiązanie z wyjaśnieniem
type Status = "nowe" | "gotowe" | "usuniete";
type Widoczny = Exclude<Status, "usuniete">;
type Zamkniety = Extract<Status, "gotowe" | "archiwum">;
const a: Widoczny = "nowe";
const b: Zamkniety = "gotowe";
console.log(a, b);
nowe gotowe
Extract zostawia część wspólną unii — archiwum nie należało do Status.
Utwórz obiekt tryby z kluczami solo i duet. Wyprowadź typ kluczy przez keyof typeof. Funkcja limit ma odczytać liczbę graczy dla wybranego trybu.
Mała podpowiedź
Nie powtarzaj ręcznie unii „solo” | „duet”.
Przykładowe rozwiązanie z wyjaśnieniem
const tryby = { solo: 1, duet: 2 } as const;
type Tryb = keyof typeof tryby;
function limit(tryb: Tryb): number { return tryby[tryb]; }
console.log(limit("duet"));
2
Typ kluczy i dane mają wspólne źródło, więc trudniej je rozjechać.
Co zabierasz do następnej lekcji
- Typy narzędziowe wyprowadzają nowe opisy z istniejących.
- satisfies sprawdza zgodność; as jest asercją, a nie walidacją.
- Zmiana typu nie zmienia automatycznie obiektu podczas działania.