09 sie 2024
4 min

Najistotniejsze zmiany wprowadzone w nowej wersji TypeScript’a 5.5

Tak jak i inne rzeczy (szczególnie w świecie technologii), TypeScript wciąż rozwija się poprzez zapewnienie najlepszych możliwych rozwiązań oraz czyniąc proces developmentu przyjemniejszym. Przejrzyjmy więc co zostało zmienione i jak możemy z tego skorzystać w codziennej pracy developera.

Wsparcie dla nowych metod

Ostatnio, w języku JavaScript zostały wprowadzone nowe metody dla obiektu Set, takie jak union() czy też difference, zapewniające natywną funkcjonalność dla typowych operacji na obiekcie Set. Wraz z wprowadzeniem wersji TypeScript 5.5, pełne wsparcie dla tychże metod zostało wprowadzone, umożliwiając programistom bezproblemowe używanie ich w projektach opartych na TypeScript. Rozważmy poniższy przypadek

const bikes = new Set(["Wheeler", "Merida"]);
const cars = new Set(["Volkswagen", "Saab"]);


const myGarage = bikes.union(car);// { 'Volkswagen', 'Saab', 'Wheeler', 'Merida'}

Jeśli korzystasz ze starszych wersji TypeScript, TypeScript compiler wskazałby następujący błąd.

Property 'union' does not exist on type 'Set<string>'.(2339)

Pamiętaj, że samo podniesienie wersji TypeScript nie rozwiąże tego problemu. Aby rozwiązać w pełni ten problem, upewnij się, że w pliku tsconfig.json ustawiłeś właściwą wersję ECMAScript.

Ulepszone Type Predicates

To uprawnienie może definitywnie zmienić sposób w jaki piszemy kod, szczególnie w przypadku kiedy pracujemy przy tablicach. Rozważmy następujący przypadek.

const grades = [3, 4, "A", "C", 2, 5];

Jak widzimy, zaprezentowana jest tablica składająca się z ocen, które mogą być zarówno w typie string jak i numer. Załóżmy, że chcemy poniższą tablicę przefiltrować przez typ oceny. W starszych wersjach TypeScript, musielibyśmy zapisać to w następujący sposób

const nonNumericGrades = grades.filter((grade): grade is string => typeof grade === 'string' );  // Output: string[]

Wraz z ulepszonymi type predicates, TypeScript automatycznie zawęża typ, a więc przefiltrowana tablica nonNumericGrades będzie wnioskowana jako string[]. bez konieczności dodatkowego wskazywania typu

const nonNumericGrades = grades.filter((grade) =>
 typeof grade === 'string'); // Output: string[]

Decydując się podnieść wersję TypeScript do wersji 5.5 musisz zachować pewną dozę ostrożności. W pewnych przypadkach możesz napotkać pewne trudności. Rozważmy następny przykład

const values = [3, false, 'string'].filter((x) => typeof x !== 'boolean');
values.push(true);

W poprzednich wersjach TypeScript ten fragment kodu zadziałałby właściwie. Wraz z updatem ujrzałbyś następujący error:

Argument of type 'boolean' is not assignable to parameter of type 'string | number'.(2345)

Dzieje się to, ponieważ obecnie TypeScript jest bardziej precyzyjny.

TypeScript 5.5:

const values: (string | number)[]

Poprzednia wersja TypeScript:

const values: (string | number | boolean)[]

Możesz pozbyć się tego błędu w następujący sposób

const values: (string | number | boolean)[] = [3, false, 'string'].filter((x) => typeof x !== 'boolean');

Wpływ na inne biblioteki

Rozszerzając poniekąd poprzednią sekcję artykułu, możemy wspomnieć, że z najnowszych usprawnień TypeScript możemy skorzystać nie tylko w czystych plikach TypeScript lecz także w plikach które korzystają z zewnętrznych bibliotek, na przykład biblioteki RxJS. Zerknijmy na następujący przykład.

class Example {
  private _myArr = of("11", 1);
}

Jak widzimy na załączonym przykładzie, utworzyliśmy observable poprzez użycie operatora  of właściwego dla biblioteki RxJS. Podobnie jak w poprzednim przykładzie chcemy przefiltrować wartość observable mając na uwadze typ wartości.

this._myArr.
  pipe(
    filter((el) => {
      return typeof el !== 'string'
    }),
    tap((filteredElement) => 
      console.log(filteredElement * 10)
    )
  ).subscribe()

Jeśli korzystalibyśmy ze starszych wersji TypeScript, ujrzelibyśmy następujący błąd.

The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.(2362)

Sprawdzanie Syntaxu Wyrażeń Regexowych

Wraz z wprowadzeniem najnowszej wersji TypeScript, wyrażenia regexowe i ich syntax nie są już pomijane. Od teraz możemy korzystać z możliwości sprawdzania syntaxu przez TypeScript. Jest to z pewnością duża zmiana, ale nadal musimy wykazać się czujnością. Sprawdzenie syntaxu jest przeprowadzane jedynie dla regularnych expression literals. Jeśli będziemy chcieli skorzystać z kontruktura obiektu RegExp, to sprawdzanie syntaxu nie zadziała.

const regPattern = /angular18(/; //error:  ')' expected.
const regExp =  RegExp(" /angular18(/"); //no error thrown


//Typescript Version Lower than 5.5:
const regPattern = /angular18(/; /no error thrown

Ulepszone zawężenie typów

Kolejnym kamieniem milowym w procesie jest ułatwienie dostępu to properties obiektu poprzez usprawnienie zawężania typów. Zerknijmy na następujący przykład:

type ObjectType = Record<number | string, number | string>;


const someObject: ObjectType = {
   10: 30
}


function multiplyVal(obj: ObjectType, key: number | string) {
   if (typeof obj[key] === "number") {
       return {
           key: obj[key] * 10
       }
   } else {
       return {
           key: 'Key is not a number'
       }
   }
}

Utworzyliśmy funkcję, która sprawdza czy wartość property obiektu jest typu number, a jeśli tak, to zwracana jest przemnożona wartość klucza. W każdym innym wypadku, obiekt zwróci property o wartości zahardcodowanego tekstu. Do tej pory, deweloperzy w przypadku napisania powyższego kodu, zwróciliby uwagę na następujący błąd:

The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.(2362)

Ten błąd nie występuje w najnowszej wersji TypeScipt. Co więcej nie musimy już obchodzić problemu w następujący sposób.

const value = Number(obj[key])
    return {
        key: value * 10
    }

Ulepszony Type Imports in JSDoc

Ostatnia wersja TypeScript wspiera nowym @Import tag. Celem wprowadzenia tego ulepszenia było ograniczenie problemów z importowanymi typami w dokumentacji, jako, że te istnieją jedynie podczas runtime naszej aplikacji. Spójrzmy: 

/** @import { ObjType } from "types" */


/**
* @param { ObjType } objExample
*/


function myFunc(objExample) {
   // ...
}

Przed ostatnimi zmianami, deweloperzy musieli rozważyć napisanie obejść takich jak te:

/**
* @param {import("Types").ObjType} objExample
*/

Obecnie, dzięki zmianom w najnowszej wersji TypeScript nie musimy już tego robić

Waga naszej paczki

Wspomnieć należy, że najnowsza wersja TypeScript została nieco ‘odchudzona’ w stosunku do poprzedniej . Udało się to osiągnąć poprzez przebudowanie plików tsserver.js i typingInstaller.js. Obecnie są one częścią publicznego API i nie produkują własnych bundlów

Before After Difference(%)
Packed 5.51 MiB 3.76 MiB 31.66%
Unpacked 30.18 MiB 20.36 MiB 32.55%

Angular a Typescript

Jeśli korzystasz z framawork’a Angular i chcesz skorzystać z najnowszych usprawnień TypeScript, musisz podnieść wersję Angulara do wersji 18.1. Od tej wersji Angulara możesz swobodnie korzystać z TypeScript 5.5. Możesz przeczytać więcej temat nowej wersji Angulara tutaj.

Podsumowanie

Jak wskazano w artykule, najnowsza wersja TypeScript wprowadziła wiele zmian. Z każdą kolejną iteracją TypeScript staje się coraz to lepszym narzędziem przydatnym w codziennej pracy developerów. Mamy nadzieję, że następna wersja TypeScript wprowadzi równie owocne zmiany co ostatnia wersja.

Dziękuję za przeczytanie mojego artykułu

Podziel się artykułem

Zapisz się na nasz newsletter

Dołącz do community Angular.love i bądź na bieżąco z trendami.