Menu

Typy literałowe i as const w TypeScript, z przykładami

Typ literałowy to typ z dokładnie jedną wartością, na przykład "GET" albo 404. Poznaj typy literałowe string, number i boolean, unie literałów, powód, dla którego let poszerza typ, a const nie, działanie as const i parametry typu const.

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

Typ literałowy to typ z dokładnie jedną wartością: "up" to typ, którego jedynym elementem jest string "up", a 404 to typ, którego jedynym elementem jest liczba 404. Same w sobie nie są zbyt przydatne. Połączone w unię dają zmienną, która przyjmuje stały zestaw wartości i nic poza nim.

Bez komentarza @ts-expect-error ostatnie wywołanie to błąd kompilacji (TS2345). Z komentarzem program się kompiluje, a wywołanie i tak się wykonuje i wypisuje moving north by 1: typy literałowe istnieją tylko dla kompilatora, a w czasie działania wartość to zwykły string.

Literały string, number i boolean

Każdą wartość typu string, number, bigint albo boolean można zapisać jako typ. Kompilator przyjmuje wtedy tylko tę dokładną wartość.

let method: "GET" = "GET";
let code: 404 = 404;
let ok: true = true;
let big: 10n = 10n;

type Port = 80 | 443 | 8080;
const port: Port = 443;

Sam boolean to po prostu unia true | false, dlatego zawężenie boolean przez if (flag) zostawia false w gałęzi else.

Typ literałowyDopuszczaSzerszy typ
"GET"tylko string "GET"string
404tylko liczbę 404number
10ntylko bigint 10bigint
truetylko trueboolean

Unie literałów

Typowe zastosowanie to unia, która wymienia każdą dozwoloną wartość. Wewnątrz funkcji kompilator zawęża unię w miarę sprawdzania, więc każda gałąź dokładnie wie, jaką ma wartość.

Unia literałów stringowych to typowa w TypeScript alternatywa dla enuma. Nic nie kosztuje w czasie działania, wartości to zwykłe stringi, które można logować i wysyłać w JSON-ie, a literówka jest błędem kompilacji. Więcej o zaletach i wadach na stronie o enumach.

Poszerzanie: let kontra const

Gdy TypeScript wnioskuje typ z literału, patrzy, czy wartość może się zmienić. Zmiennej const nigdy nie da się przypisać na nowo, więc zachowuje typ literałowy. Zmienną let da się, więc jej typ zostaje poszerzony do typu ogólnego.

Najedź kursorem na każdą nazwę w edytorze, żeby zobaczyć wywnioskowany typ. Jeśli chcesz, żeby let przechowywało tylko określone wartości, oznacz jego typ: let mode: "light" | "dark" = "light".

Dlaczego właściwości obiektów się poszerzają

Właściwości literału obiektowego są modyfikowalne, więc też się poszerzają, nawet gdy obiekt jest zapisany w const. To najczęstszy sposób, w jaki trafia się na typy literałowe przypadkiem:

Kompilator zgłasza:

index.ts(7,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

req ma wywnioskowany typ { url: string; method: string }, bo późniejszy kod mógłby wykonać req.method = "DELETE". Są trzy rozwiązania:

Czwarta opcja to satisfies, które sprawdza obiekt względem typu, a jednocześnie zachowuje literałowe typy jego właściwości.

as const

as const to const assertion. Umieść je po wyrażeniu, a kompilator wywnioskuje najwęższy możliwy typ:

  • wartości string, number i boolean zachowują typy literałowe
  • właściwości obiektów stają się readonly
  • literały tablic stają się krotkami readonly o stałej długości

Asercja działa tylko w czasie kompilacji. Wygenerowany JavaScript to ten sam literał obiektowy bez as const, więc nic nie powstrzyma innego kodu przed modyfikacją go w czasie działania. Jeśli potrzebujesz gwarancji w czasie działania, wywołaj też Object.freeze.

Typ unii z tablicy as const

Częsty wzorzec to trzymanie dozwolonych wartości w jednej tablicy, po której można iterować w czasie działania, i wyprowadzanie z niej typu unii. (typeof arr)[number] oznacza „typ dowolnego elementu arr”.

Bez as const ROLES miałoby typ string[], a Role byłoby zwykłym string. Rzutowanie na readonly string[] w isRole jest potrzebne, bo includes na krotce literałów przyjmuje tylko te literały, a funkcja ma właśnie sprawdzić string, który może nie być żadnym z nich. Ten sam wzorzec z obiektem działa dla map klucz/wartość: const Status = { Active: "active", Banned: "banned" } as const, a potem type Status = (typeof Status)[keyof typeof Status].

Parametry typu const

Funkcja generyczna zwykle poszerza przekazane jej literały. Od TypeScript 5.0 można oznaczyć parametr typu jako const, dzięki czemu kompilator wnioskuje argument tak, jakby miał as const, bez proszenia wywołującego o dopisanie tego.

To głównie narzędzie dla autorów bibliotek: definicje tras, buildery i funkcje pomocnicze do schematów korzystają z niego, żeby wywołujący dostawali dokładne typy ze zwykłych literałów.

Znaczenia const

Słowo kluczowe const pojawia się w kodzie TypeScript w czterech różnych miejscach:

SkładniaRodzajCo robi
const x = 1deklaracja JavaScriptuzmiennej nie można przypisać na nowo; wartość literałowa zachowuje typ literałowy
expr as constasercja TypeScriptnajwęższy typ: literały, właściwości readonly, krotki readonly
function f<const T>()parametr typu TypeScriptwnioskuje argumenty tak, jakby miały as const
const enum E {}enum TypeScriptenum, którego składowe są wstawiane w miejsce użycia podczas kompilacji

Żadne z nich nie zamraża obiektu w czasie działania. const obj = { a: 1 } nadal pozwala na obj.a = 2; błędem jest tylko ponowne przypisanie samego obj.

Najczęściej zadawane pytania

Czym jest typ literałowy w TypeScript?

To typ, który dopuszcza dokładnie jedną wartość. "GET" to typ, którego jedyną wartością jest string "GET", 404 to typ, którego jedyną wartością jest liczba 404, a true to typ, którego jedyną wartością jest true. Najbardziej przydają się połączone w unie, takie jak type Method = "GET" | "POST".

Co robi as const w TypeScript?

as const to const assertion. Każe kompilatorowi wywnioskować najwęższy typ wyrażenia: wartości string i number zachowują typy literałowe, właściwości obiektów stają się readonly, a literały tablic stają się krotkami readonly. Zmienia tylko typ; wartość w czasie działania to ten sam zwykły obiekt albo tablica i nie jest zamrożona.

Dlaczego TypeScript wnioskuje string zamiast mojego literału?

Bo wartość jest modyfikowalna. let x = "a" i właściwość w { method: "GET" } można później przypisać na nowo, więc TypeScript poszerza je do string. Zmienna const zachowuje typ literałowy "a". Żeby zachować literały w obiekcie, oznacz go typem literałowym, użyj as const albo satisfies.

Czym różni się const od as const?

const to deklaracja JavaScriptu: zmiennej nie można przypisać na nowo, ale obiekt, na który wskazuje, nadal można zmieniać. as const to asercja typu TypeScript: sprawia, że każda właściwość wartości jest w systemie typów readonly i literałowa. Żadne z nich nie zamraża obiektu w czasie działania; do tego służy Object.freeze.

Jak uzyskać typ unii z tablicy stringów?

Zadeklaruj tablicę z as const, a potem zaindeksuj jej typ przez number: const roles = ["admin", "user"] as const; type Role = (typeof roles)[number]; daje "admin" | "user". Bez as const tablica ma typ string[], a wynikiem jest zwykły string.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ