Od wydania poprzedniej dużej wersji TypeScript minęło już prawie trzy lata. Z tego powodu wydanie TypeScript 5.0 to niemałe wydarzenie! Wraz z nową wersją otrzymaliśmy wreszcie dekoratory zgodne ze standardem ECMAScript i kilka innych ciekawych nowości.
1. TypeScript 5.0
Migracja do modułów
Największą nowością w TypeScript 5.0 jest migracja kodu z namespaces do modułów. Wiąże się z nią znaczące odchudzenie paczki i lekka poprawa wydajności. Decyzja o migracji podyktowana była potrzebami społeczności, która od lat korzysta już z modułów. TypeScript stosuje technikę zwaną Dog Feeding, czyli w ramach testów kompiluje sam siebie. Pomimo tego, że język w ostatnich latach wprowadzał sporo nowości jeśli chodzi o moduły, to sam nie mógł z nich skorzystać. Ponadto nie korzystając z modułów, zespół tracił okazję do testów funkcjonalności mocno wykorzystywanej przez społeczność.
Jeśli swoją przygodę z TypeScript zaczynaliście na przestrzeni kilku ostatnich lat, to istnieje spore prawdopodobieństwo, że nigdy nie mieliście styczności z namespaces. Funkcjonalność ta powstała w 2014 roku, kiedy JavaScriptowe moduły były jeszcze w powijakach, a o miano standardu rywalizowało kilka konwencji. TypeScript pilnie potrzebował rozwiązać problem modularyzacji i zanieczyszczenia przestrzeni nazw, dlatego zaproponował swoje autorskie rozwiązanie. W dużym skrócie, namespace to słodzik syntaktyczny na stwrzenie globalnego obiektu.
// TypeScript input
namespace Example {
export const name = 'Tomek';
}
// JavaScript output
var Example;
(function (Example) {
Example.name = 'Tomek';
})(Example || (Example = {}));
W 2015 roku do standardu ECMAScript trafiły wrescie pełnoprawne i dobrze zaporojektowane moduły. Rozwiązywały one nie tylko problem zanieczyszczenia przestrzeni nazw, ale dawały też narzędzia pozwalające kontrolować w jaki sposób nasza aplikacja jest dzielona na kawałki i kiedy kawałki te są ładowane. Tym samym TypeScriptowe namespaces stały się niepotrzebne i szybko zapomniane przez społeczność.
Z migracją TypeScript do modułów wiąże się naprawdę sporo ciekawych historii. Od wyboru odpowiednieg Bundlera, przez problemy wydajnościowe związane z stosowaniem let
i const
, po odchudzenie rozmiaru paczkie przez zmniejszenie wcięć. Jeśli macie ochotę zagłębić się trochę w temat, to zdecydowanie polecam krótkie case study przygotowane przez Microsoft.
Dekoratory
Historia dekoratorów w TypeScript to materiał na kilku sezonowy serial pełen zwrotów akcji i cliffhangerów. Przygoda zaczyna się w październiku 2014 roku, kiedy to na spotkaniu TC39 (przyp: grupa standaryzacyjna JavaScript, ja lubię ją porównywać do plemiennej starszyzny) zaprezenotwany został pierwszy proposal dekoratorów. Właściwie równolegle, na konferencji ngEurope ogłoszono, że Angular 2.0 napisany będzie w AtScript. Nowy język od Google maiał być oparty o TypeScript, ale rozszerzać go o kilka nowych funkcjonalności. Wśród nich znajdowały się między innymi tytułowe dekoratory.
Zasadnym wydaje się pytanie, dlaczego Google nie zdecydował się po prostu zainwestować w rozwój TypeScriptu. Stało się tak ze względu na konflikt interesów. Zespołowi z Microsoft zależało, żeby TypeScript był tak blisko JavaScriptu jak to tylko możliwe. To właściwie wykluczało szanse, na dodanie funkcjonalności, która w przyszłości mogłaby kolidować ze standardem. Zespół z Google potrzebował narzędzi, które pozwolą im zbudować ich wymarzony framework. Bez dekoratorów TypeScipt nie był takim narzędziem.
Na szczęście po burzliwych negocjacjach obie firmy doszyły do porozumienia. W maju 2015 roku na jednej z europejskich konferencji ogłoszono, że do TypeScript trafią dekoratory zgodne z Proposalem znajdującym się w Stage 1 (przyp: proces standaryzacyjny JavaSciprt składa się z 4 etapów – dopiero funkcjonalności w 3 etapie uznaje się za w miarę stabilne). Podczas tej samej konferencji ogłoszono, że Angular zostanie jednak opraty o TypeScript.
Od 2015 roku w kwestii dekoratorów sporo się zmieniło. Przede wszystki proposal przedarł się do Stage 3. Nie obyło się jednak bez strat. Z oryginalnego proposala wycięto znaczną część dotyczącą metadanych. To oznacza, że dekoratory będące już u progu standardu JavaScript, nie są zgodne z tymi zaimplementowanymi w TypeScript. Na szczęście na ratunek nam wszystkim przyszedł TypeScript 5.0.
Jeśli do tej pory nie mieliście doczynienia z dekoratorami, to są to po prostu funkcje, które pozwalają nam modyfikować zachowanie innch funckji lub klas. Temat jest na tyle skomplikowany i szeroki, że nie podejmę się przybliżenia go na łamach tego przegląd. Wszystkoch głodnych wiedzy odsysłam do świetnego artykułu Axela Rauschmayer. Po jego przeczytaniu możecie udać się prosto do notatki od Microsoftu, z której dowiecie się jak otypować swoje dekoratory.
// Decorator definition
function debut(originalMethod: any, _context: ClassMethodDecoratorContext) {
function replacementMethod(this: any, ...args: any[]) {
console.log("LOG: Entering method.")
const result = originalMethod.call(this, ...args);
console.log("LOG: Exiting method.")
return result;
}
return replacementMethod;
}
// Decorator usage
class Person {
constructor(private readonly name: string) {}
@debug greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
new Person("Tomek").greet();
// Expected output:
// LOG: Entering method.
// Hello, my name is Tomek
// LOG: Exiting method.
Co z projektami wykorzystującymi starą implementację dekoratorów (m.in. Angular, Ember i MobX)? Tak długo jak w konfiguracji TypeScript znajdować się będzie flaga --experimentalDecorators
ich zachowanie nie ulegnie zmianie. W dłuższej perspektywie wszystkie te biblioteki czeka jednak migracja.
const
Type Parametrs
Jeśli na codzień pracujecie z TypeScript, to na pewno znacie trik z as const
, który pozwala nam zawęzić inferowany typ.
// Inferred type: string[]
const namesA = ["Alice", "Bob", "Eve"];
// Inferred type: readonly ["Alice", "Bob", "Eve"]
const namesB = ["Alice", "Bob", "Eve"] as const;
Trik ten często wykorzystujemy na przykład do zawężania typu zwracanego przez funkcję.
function getNames<T extends { name: readonly string[]}>(arg: T): T["names"] {
return arg.names;
}
// Inferred type: string[]
const namesA = getNames({ names: ["Alice", "Bob", "Eve"]});
// Inferred type: readonly ["Alice", "Bob", "Eve"]
const namesB = getNames({ names: ["Alice", "Bob", "Eve"]} as const);
TypeScript 5.0 wprowadza mechanizm, który pozwala nam przenieść as const
do definicji typu. W ten sposób zabieramy ten ciężar z pleców naszych użytkowników i bierzemy go na swoje barki.
/* 👇 Hese is the new const type parameter */
function getNames<const T extends { names: readonly string[] }>(arg: T): T["names"] {
return arg.names;
}
// Inferred type: readonly ["Alice", "Bob", "Eve"]
const names = getNames{ names: ["Alice", "Bob", "Eve"] });
I wiele więcej…
Oczywiście to nie koniec nowości w TypeScript 5.0. Wszystkich zainteresowanych szczegółami najnowszej wersji języka odsyłam do świetnej notatki przygotowanej przez Microsoft.
Zainstaluj teraz i czytaj tylko dobre teksty!
2. Lazy Loading w React Router 6.9
React Router z wersji na wersję staje się coraz bardziej rozbudowaną biblioteką. W React Router 6.4 w nasze ręce trafiły loadery, które umożwiliają nam zrównoleglenie pobierania danych dla wszystkich potrzebnych komponentów./pr
const routes = [{
path: '/',
loader: () => getUser(),
element: <Layout />,
children: [{
path: 'projects',
loader: () => getProjects(),
element: <Projects />,
children: [{
path: ':projectId',
loader: ({ params }) => getProject(params.projectId),
element: <Project />,
}],
}],
}]
W wydanym w tym tygodniu React Router 6.9 w nasze ręce trafiła funkcja React.lazy
, która umożliwia zrównoleglenie pobierania komponentów. Łącząc ją z opizanymi w poprzednim akapicie loaders
, możemy zrównoleglić zarówno pobieranie danych jak i komponentów.
const routes = [{
path: '/',
element: <Layout />,
children: [{
path: 'projects',
loader: () => getProjects(),
lazy: () => import("./projects"), // 💤 Lazy load!
children: [{
path: ':projectId',
loader: ({ params }) => getProject(params.projectId),
lazy: () => import("./project"), // 💤 Lazy load!
}],
}],
}]
Jeśli temat Was zainteresował, to na blogu React Router opublikowany został artykuł, który w detalach opisuje jakie możliwości zrównoleglenia pobierania oferuje nam biblioteka.
3. Nowa dokumentacja Reacta
React doczekał się wreszcie dokumentacji na jaką zasługuje. Po długich miesiącach w becie, nowa i przepisana od zera dokumentacja, staje się tą domyślna. Przestarzała już poprzednicza nie znika z internetu i dostępna będzie pod adresem legacy.reactjs.org. Co wyróżnia nową dokumentacją? Przede wszystkim jest ona bogata w interaktywne przykłady i posiada odświeżoną szatę graficzną. Jeśli jeszcze jej nie widzieliście, to najwyższa pora to nadrobić. Zwłaszcza, że została ona uzupełniona o kilka ciekawych zaawansowanych zagadnień (np. dlaczego powinniśmy unikać useEffect
).
Zainstaluj teraz i czytaj tylko dobre teksty!
4. ECMAScript® 2023
W minionym tygodni do sieci trafiła nowa specyfikacja JavaScript. Jak zwykle nie jest to lekka lektura i zanim do niej przystąpicie polecam zapoznać się z poradnikiem czytania specyfikacji ECMAScript przygotowanym przez Timothy Gu. Jeśli mimo wszystko lektura okaże się dla Was za trudna, to w zeszłym tygodniu Reddita podbił artykuł Linusa Schlumbergera, który świetnie podsumowywuje wszystkie nowości w JavaScript z ostatnich trzech lat.