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.

CEL LEKCJI

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.
WPROWADZENIE

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.

POZNAJ SKŁADNIĘ

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.

src/index.ts — osobny przykład
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);
wynik w konsoli
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.

src/index.ts — osobny przykład
type Rola = "gracz" | "moderator";
const limity: Readonly<Record<Rola, number>> = { gracz: 3, moderator: 10 };
console.log(limity.gracz, limity.moderator);
wynik w konsoli
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.

PRZYKŁAD Z OMÓWIENIEM

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.

src/index.ts — osobny przykład
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);
wynik w konsoli
Ala Pixel 1
3

Jak program dochodzi do wyniku

  1. Pick dopuszcza tylko nick oraz opis w typie zmiany.
  2. Partial pozwala podać jedno z tych pól lub żadne.
  3. Jawne składanie wyniku zachowuje id także przy obiekcie wejściowym posiadającym dodatkowe pola.
  4. 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ę.

EKSPERYMENT

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.

src/index.ts — osobny przykład
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);
wynik w konsoli
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.

CZĘSTE BŁĘDY

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ć.
ZADANIA

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.

ZAD 1Publiczna wizytówka★☆☆

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
src/index.ts — osobny przykład
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);
wynik w konsoli
Ala

Publiczny model nie wymaga id.

ZAD 2Konfiguracja poziomów★★☆

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
src/index.ts — osobny przykład
const poziomy = { easy: 2, normal: 5, hard: 10 } satisfies Record<"easy" | "normal" | "hard", number>;
console.log(poziomy.hard);
wynik w konsoli
10

Zamknięta unia kluczy wymaga kompletnej konfiguracji.

ZAD 3Wybór statusów★★☆

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
src/index.ts — osobny przykład
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);
wynik w konsoli
nowe gotowe

Extract zostawia część wspólną unii — archiwum nie należało do Status.

ZAD 4Nazwy trybów z danych★★★

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
src/index.ts — osobny przykład
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"));
wynik w konsoli
2

Typ kluczy i dane mają wspólne źródło, więc trudniej je rozjechać.

PODSUMOWANIE

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.