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