Menu

Operator satisfies w TypeScript: a adnotacje typów i as

Operator satisfies sprawdza, czy wartość pasuje do typu, nie zmieniając jej wywnioskowanego typu. Zobacz, co robi, jak wypada na tle adnotacji typu i as (ten sam obiekt zapisany na trzy sposoby), jak łączy się z as const i dlaczego pasuje do obiektów konfiguracji.

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

value satisfies Type sprawdza podczas kompilacji, czy value pasuje do Type, a potem zostawia własny, bardziej precyzyjny typ wartości w spokoju. Adnotacja zastąpiłaby ten precyzyjny typ przez Type; satisfies weryfikuje bez poszerzania.

satisfies nadal sprawdza: brakujący kolor, błędnie wpisany klucz, taki jak bleu, albo wartość w rodzaju true to błąd kompilacji w tej linii. Istnieje od TypeScript 4.9 i, jak każda adnotacja typu, jest usuwany z wynikowego JavaScriptu.

Problem, który rozwiązuje satisfies

Z adnotacją typu typ zmiennej jest adnotacją. Kompilator zapomina, co widział w literale. Tu ta sama paleta ma zamiast tego adnotację i TypeScript już nie wie, że green to string:

Kompilator zgłasza:

index.ts(11,27): error TS2339: Property 'toUpperCase' does not exist on type 'Color'.
  Property 'toUpperCase' does not exist on type '[number, number, number]'.

Przed TypeScript 4.9 do wyboru było: dodać adnotację i wszędzie zawężać ręcznie (typeof palette.green === "string") albo pominąć adnotację i stracić sprawdzenie. satisfies daje jedno i drugie. Zamień : Record<ColorName, Color> na satisfies Record<ColorName, Color> po zamykającym nawiasie klamrowym, a kod się uruchomi.

satisfies a adnotacja typu a as

Ten sam obiekt ustawień zapisany na trzy sposoby:

as przepuścił brakujące lang, a asserted.lang w czasie działania to undefined, choć jego typ mówi string. Usuń lang z dwóch pozostałych linii, a obie zakończą się błędem TS2741, Property 'lang' is missing in type ....

Adnotacja const x: T = vAsercja v as Tv satisfies T
Brakujące właściwościbłąddozwolonebłąd
Nadmiarowe właściwości (literał obiektu)błąddozwolonebłąd
Zły typ właściwościbłądtylko gdy typy się nie pokrywająbłąd
Typ x potemTTwywnioskowany typ v
Typy literałowe ("dark", 8080)poszerzone do Tposzerzone do Tzachowane, gdzie T na nie pozwala
Klucze Record<string, ...>dowolny string (literówki się kompilują)dowolny stringdokładnie zapisane klucze
Efekt w czasie działaniabrakbrakbrak

Praktyczna zasada: dodawaj adnotację, gdy chcesz, żeby zmienna miała zadeklarowany typ (wartość, którą będziesz ponownie przypisywać, publiczne API), a satisfies używaj, gdy chcesz sprawdzenia, ale własny typ wartości jest bardziej przydatny.

Wyłapywanie błędów w literałach obiektów

satisfies wykonuje pełne sprawdzenie przypisywalności, łącznie ze sprawdzaniem nadmiarowych właściwości, więc literówki w kluczach to błędy:

type Route = { path: string; method: "GET" | "POST" };

const home = { path: "/", metod: "GET" } satisfies Route;
// error TS2561: Object literal may only specify known properties, but 'metod' does not exist in type 'Route'. Did you mean to write 'method'?

Sprawdzenie nadaje też literałowi typ kontekstowy, tak jak adnotacja. Ma to znaczenie na dwa sposoby. Literały tekstowe zostają typami literałowymi, gdy typ docelowy ich oczekuje: { path: "/", method: "GET" } satisfies Route ma method: "GET", podczas gdy ten sam obiekt bez adnotacji dałby method: string. A parametry callbacków są wnioskowane z typu docelowego:

Klucze Record pozostają znane

Częste zastosowanie to tablica wyszukiwania. Z adnotacją Record<string, T> każdy string jest poprawnym kluczem, a literówka się kompiluje i w czasie działania zwraca undefined. Z satisfies wartości nadal są sprawdzane względem T, ale typ zmiennej wymienia dokładnie te klucze, które zapisano:

keyof typeof endpoints jest przydatne tylko dlatego, że klucze przetrwały. Z adnotacją byłoby zwykłym string.

Aby wymagać stałego zbioru kluczy, użyj satisfies z Record po unii: satisfies Record<"dev" | "prod", string> zgłasza brakujące prod błędem TS2741, a nieznane staging błędem TS2353.

as const satisfies

as const i satisfies można łączyć. Pisz najpierw as const: czyni wartość głęboko readonly z typami literałowymi, a potem satisfies sprawdza tę dokładną wartość.

Każda trasa jest sprawdzana względem Route (method: "PUT" byłoby błędem), a krotka typów literałowych pozostaje dostępna, więc Path to unia prawdziwych ścieżek. Jako typu docelowego użyj readonly Route[] (albo ReadonlyArray<Route>), bo tablica as const jest readonly.

Obiekty konfiguracji

To w konfiguracji satisfies naprawdę się opłaca: kształt musi się zgadzać, a kod w innych miejscach potrzebuje precyzyjnych wartości.

Pomiń wpis production, pomyl pisownię logLevel albo napisz logLevel: "verbose", a kompilator wskaże dokładną linię. Ten sam wzorzec pasuje do plików *.config.ts: export default { ... } satisfies SomeConfig sprawdza cały plik, a eksportowany obiekt zachowuje swoje wartości literałowe.

Kiedy nie używać satisfies

  • Zmienna będzie ponownie przypisywana. let cfg = { port: 3000 } satisfies { port: number | string } nadaje cfg typ { port: number }, więc późniejsze cfg = { port: "80" } się nie uda (TS2322). Dodawaj adnotacje do zmiennych, które zamierzasz zmieniać.
  • Celowo chcesz zadeklarowanego typu. Dla wartości zwracanej funkcji albo eksportowanej stałej, która jest częścią API, typ z adnotacji jest kontraktem, a ujawnienie dokładnego typu literałowego może sprawić, że późniejsze zmiany staną się breaking changes.
  • Wartość nie jest literałem. satisfies błyszczy na literałach obiektów i tablic. Na zmiennej lub wyniku wywołania to zwykłe sprawdzenie przypisywalności, które daje już adnotacja.

Najczęściej zadawane pytania

Co robi satisfies w TypeScript?

expression satisfies Type sprawdza podczas kompilacji, czy wyrażenie jest przypisywalne do Type, zgłaszając brakujące właściwości, nadmiarowe właściwości i złe typy wartości, a potem zostawia własny wywnioskowany typ wyrażenia bez zmian. Dostajesz bezpieczeństwo adnotacji i precyzję wnioskowania. Jest usuwany z wynikowego JavaScriptu.

Czym różni się satisfies od adnotacji typu?

Oba sprawdzają wartość. Adnotacja (const x: T = ...) nadaje potem zmiennej typ T, zapominając, co kompilator wiedział o wartości (typy literałowe, który element unii ma każda właściwość, jakie klucze istnieją). satisfies T zachowuje wywnioskowany typ, więc wiadomo, że x.someKey istnieje, a właściwość string | number przechowująca string ma typ string.

Czym różni się satisfies od as w TypeScript?

as to asercja: nadpisuje typ i prawie niczego nie sprawdza, więc brakujące właściwości przechodzą niezauważone. satisfies to sprawdzenie: wartość musi naprawdę pasować do typu, a jej własny wywnioskowany typ zostaje zachowany. Gdy oba by się skompilowały, satisfies jest bezpieczniejszym wyborem.

Co oznacza as const satisfies?

Stosuje oba: as const czyni wartość głęboko readonly z typami literałowymi, a potem satisfies sprawdza ten wynik względem typu. Pisz najpierw as const: const routes = [...] as const satisfies readonly Route[];. Zmienna zachowuje dokładne typy literałowe do późniejszego użycia, a zły wpis nadal jest błędem kompilacji.

W której wersji TypeScript dodano satisfies?

W TypeScript 4.9, wydanym w listopadzie 2022. To zwykła składnia, którą da się usunąć, więc działa też z wbudowanym type strippingiem w Node, a obsługuje ją każda aktualna wersja TypeScriptu (łącznie z 7).

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ