Kategoria: TypeScript

Stałe Typy w TypeScript: Przewodnik po 'as const'


Wyrażenie as const jest szczególnie użyteczne, gdy chcemy zadeklarować stałe typy w naszym kodzie. W tym artykule omówimy, czym jest to wyrażenie, jak go używać i jakie korzyści może przynieść.

Czym jest as const?

as const to wyrażenie w TypeScript, które pozwala zadeklarować stałe typy. Dzięki niemu można określić, że dana wartość nie może być zmieniana po jej zadeklarowaniu. W efekcie TypeScript może śledzić te stałe typy i wykrywać potencjalne błędy w czasie kompilacji.

Użycie as const z tablicami

Popatrzmy na poniższy przykład:

const numbers = [1, 2, 3] as const

W tym przypadku as const wskazuje, że tablica numbers powinna być traktowana jako wartość readonly. Oznacza to, że nie możemy jej później zmodyfikować.

// To spowoduje błąd kompilacji
numbers.push(10) // Property 'push' does not exist on type 'readonly [1, 2, 3]'
number[0] = 5 // Cannot assign to '0' because it is a read-only

Użycie as const z obiektami

Następnym przypadkiem użycia wyrażenia as const jest deklaracja stałych obiektów.

const person = {
  firstName: "John",
  lastName: "Doe",
} as const

Wykorzystanie as const w tym przypadku sprawia, że zarówno obiekt person jak i jego właściwości firstName i fullName są stałe. To oznacza, że nie możemy zmieniać wartości tych właściwości ani dodawać nowych.

// To spowoduje błąd kompilacji
person.firstName = "Jane" // Cannot assign to 'firstName' because it is a read-only
person.age = 30 // Property 'age' does not exist on type '{ readonly firstName: "John"; readonly lastName: "Doe"; }'

Sprawdźmy do czego jeszcze możemy w praktyce wykorzystać as const. Weźmy taki obiekt:

const endpoints = {
  blog: '/blog',
  aboutUs: '/about-us',
  contact: '/contact'
}

Teraz chcemy stworzyć funkcję, która jako parametr przyjmie jedną z wartości z powyższego obiektu, jeśli chcemy to mocno typować możemy to zapisać w taki sposób:

const getEndpointData = (endpoint: '/blog' | '/about-us' | '/contact') => { }

Jednak jeśli spróbujemy teraz wywołać tę funkcję podając w parametrze jedną właściwości obiektu endpoints otrzymamy błąd:

const data = getEndpointData(endpoints.contact)
// Argument of type 'string' is not assignable to parameter of type '"/blog" | "/about-us" | "contact"'

Dzieję się tak, ponieważ Typescript właściwości tego obiektu traktuje jako jakikolwiek string, ponieważ w każdym dowolnym momencie te właściwości mogą zostać nadpisane, dlatego nie muszą one spełniać warunku '"/blog" | "/about-us" | "contact"'. Z pomocą przychodzi as const. Spójrz na cały poprawiony zapis:

const endpoints = {
  blog: '/blog',
  aboutUs: '/about-us',
  contact: '/contact'
} as const

const getEndpointData = (endpoint: '/blog' | '/about-us' | '/contact') => { }

const data = getEndpointData(endpoints.contact)

Taki zapis nie zwróci żadnego błędu. Obiekt endpoints zadeklarowany jest jako stała i nie można zmieniać jego właściwości, dlatego spełniony jest typ podany przy parametrze w funkcji getEndpointData.

Pozostaje jeszcze jeden problem, ponieważ może się wydarzyć, że w deklaracji obiektu chcemy zmienić wartość jakiejś właściwości. Powiedzmy, że wartość /contact zmieniła się na /contact-us. W tym przypadku znowu natkniemy się na błędy, w końcu typ parametru funkcji getEndpointData oczekuje wartości contact, a nie contact-us. Możemy zastosować tu trochę magii TypeScript:

const endpoints = {
  blog: '/blog',
  aboutUs: '/about-us',
  contact: '/contact-us'
} as const

type Endpoints = (typeof endpoints)[keyof typeof endpoints]
// to zwróci "/blog" | "/about-us" | "/contact-us"

const getEndpointData = (endpoint: Endpoints) => {

}

const data = getEndpointData(endpoints.contact)

W powyższym kodzie dodaliśmy linię type Endpoints = (typeof endpoints)[keyof typeof endpoints], taki zapis powoduje, że typ Endpoints zwróci wartości wszystkich właściwości obiektu endpoints. Dzięki temu przy każdej zmianie czy też dodaniu nowych wartości w deklaracji obiektu, typ Endpoints będzie zawierał w sobie aktualne dane.

Podsumowanie

Narzędzie as const daje nam pewność, że nasze stałe wartości pozostaną stałe i nie zostaną przypadkiem zmienione, co pomaga w zapobieganiu nieoczekiwanym błędom w czasie wykonywania programu. Dodatkowo, as const wpływa na optymalizację kodu, co może przyspieszyć naszą aplikację, oraz zwiększa czytelność kodu, ułatwiając jego zrozumienie dla innych programistów. Przy właściwym użyciu as const staje się narzędziem wspomagającym w utrzymaniu wysokiej jakości kodu, a taże niezawodności naszych aplikacji.