Niedawno na naszym YouTube’owym kanale Przeprogramowanych opowiadałem o procesie zgodnie z którym język ECMAScript ulega kolejnym przemianom. Jak wiesz (o ile oczywiście obejrzałeś ten film, ale nie wyobrażam sobie, żeby było inaczej ;) ) za wszystkim stoi tak zwany proces TC39, dzięki któremu przedstawiciele wybranych organizacji tworzących rozwiązania dla internetu proponują kolejne zmiany z których później możemy korzystać w naszych przeglądarkach.

W dzisiejszym artykule pokażę ci wybrane z tzw. “proposali” których ja sam nie mogę się doczekać, i które znacząco wpłyną na to w jaki sposób tworzymy nasze rozwiązania w obszarze JavaScriptu. Gotowi?

Kwestia czasu - Stage 3

Opisane w tej sekcji propozycje zmian znajdują się już na przedostatnim (trzecim) etapie w całym procesie uznania takiej zmiany za część standardu. O co już niebawem wzbogacą się nasze przeglądarki?

Optional Chaining

cannot read property name of undefined

Kojarzycie tego bandytę? Eh! Ile to razy tego typu błąd widziałem w konsoli swojej przeglądarki!

JavaScript nie daje nam aktualnie łatwego sposobu na poruszanie się w środowisku obiektów posiadających wiele zagnieżdżonych propertisów, więc kod na który możecie się natknąć wielokrotnie będzie wyglądał na przykład tak:

if (profile && profile.details && profile.details.firstName) {
   ...
}

Powodem tak skomplikowanego wyrażenia jest fakt, że chcemy w bezpieczny sposób dotrzeć do zagnieżdżonej właściwości danego obiektu. Niektórzy ratują się tutaj lodashem, niektórzy idąc strukturą obiektu "w dół" walidując całe drzewo "propertisów" - koniec końców ból jest ten sam.

Na szczęście lekarstwo już niedaleko. Dzięki optional chaining całość zamieni się na:

if (profile?.details?.firstName) {
  ...
}

Property profile jest niepuste? Przejdź dalej. Property details jest niepuste? Przejdź dalej. firstName istnieje? Wykonaj to co znajduje się wewnątrz ifa.

Prawda, że proste? Dodatkowo, już niebawem tę samą składnię znajdziemy w języku TypeScript! Więcej o tej zmianie przeczytasz tutaj

Top-level await

Jeśli mieliście już okazje pracować ze składnią async / await to pewnie zdajecie sobie sprawę z jej ograniczeń. Prawdopodobnie największym utrudnieniem jest fakt, że "każdy await musi mieć odpowiadający mu async", co nie zawsze jest tak łatwe w zaimplementowaniu jak mogłoby się wydawać. Poziom trudności wzrasta jeszcze bardziej jeśli do całego zadania dodamy importy oraz exporty i zechcemy się takim kodem dzielić z innymi.

Zmiana którą wprowadza ten proposal polega na wprowadzeniu tzw. "top level await" który byłby umieszczany nad całą zawartością danego modułu, dzięki czemu operacje asynchroniczne wewnątrz niego mogłyby być obsługiwane automatycznie przez dane środowisko uruchomieniowe.

Niby nic, ale uwierzcie mi - wpłynie to znacząco na adopcję asynców oraz awaitów w rozwiązaniach które budujemy na codzień.

🚀 Zobacz więcej propozycji zmian na etapie trzecim

Jeszcze trochę - Stage 2

Na etapie drugim znajdują się zmiany posiadające formalny opis oraz osobę reprezentującą taką nowość w całym procesie, ale są jeszcze obiektem dyskusji, np. osób odpowiedzialnych za przegląd takiej zmiany (tzw. reviewers).

Dekoratory

Dekoratory to jedna z moich ulubionych własności wielu języków programowania i już nie mogę się doczekać aż JavaScript będzie ją wspierał w sposób natywny. Dlaczego?

Chociażby dlatego, że wielokrotnie pisałem chociażby taki kawałek kodu:

class ProfileService {

    saveFirstName(name) {
        // 1. zaktualizuj stan aplikacji
        this.myStateManager.updateName(name);
        // 2. śledź zdarzenie w zewnętrznym systemie
        this.googleAnalyticsService.notifyFirstNameChanged();
    }

}

Niby nic strasznego, a jednak code smell. Wielokrotnie zdarzało mi się mieszać logikę biznesową aplikacji z logiką innego typu. Pierwszym z brzegu przykładem jest chociażby Google Analytics czy dowolny system zewnętrzny do analizy zachowań użytkownika.

Dzięki dekoratorom napisałbym to pewnie w nastepujący sposób:

class ProfileService {

    @track('name changed')
    saveFirstName(name) {
        // 1. zaktualizuj stan aplikacji
        this.myStateManager.updateName(name);
    }

}

Logika aplikacji pozostaje na swoim miejscu, a wszystkie operacje techniczne umieszczamy w dekoratorach. Jest to zmiana która w znaczący sposób poprawiłaby czytelność kodu oraz pomogła szybciej odnajdywać się w poszczególnych fragmentach aplikacji.

TypeScript pokazał, że się da. Czekam na JavaScript!

throw expressions

Rzucanie wyjątków poprzez throw nie może być aktualnie realizowane jako wyrażenie języka JavaScript. Nie użyjemy throw jako wartości zwracanej z funkcji, ani nie zastosujemy jej np. w okolicy parametru metody. Taki kod będzie uznany za niepoprawny:

function shouldBeOverriden () {
    return throw 'no!';
}

...o tym fakcie od razu poinformuje nas przeglądarka:

Uncaught SyntaxError: Unexpected token 'throw'

No tak, ale gdzie właściwie chcielibyśmy użyć takiej składni? Zobaczmy na następujący fragment kodu z dokumentacji tej zmiany:

function getEncoder(encoding) {
    const encoder = encoding === "utf8" ? new UTF8Encoder() 
                                : encoding === "utf16le" ? new UTF16Encoder(false) 
                                : encoding === "utf16be" ? new UTF16Encoder(true) 
                                : throw new Error("Unsupported encoding");
}

Deklarujemy funkcję która sprawdza kolejne warunki związane z przekazanym parametrem. Jeśli żaden z nich nie zostanie spełniony, to rzucony może zostać wyjątek. Wygodne!

Kolejny przypadek użycia może dotyczyć chociażby braku przekazanego parametru:

function save(filename = throw new TypeError("Argument required")) {
}

Prawda, że ma to sens?

✅ Zobacz więcej propozycji zmian na etapie drugim

Pierwsze szkice - Stage 1

Stage 1 to etap który zawiera zmiany mogące być jeszcze na etapie wysokopoziomowej dyskusji. Bez formalnego opisu, ale za to z tzw. “championem” przeprowadzającym taką nowość przez kolejne kroki procesu TC39. Czym warto zainteresować się w tej sekcji?

Partial application

Aplikowanie częściowe to jedna ze zmian które mają wprowadzić jeszcze więcej dobrego dla fanów programowania funkcyjnego. Jeśli do tej pory używaliście do tego celu takich konstrukcji jak bind albo zewnętrznych bibliotek jak Ramda, to zmiana o której piszę pomoże wam budować funkcje wyższego rzędu w jeszcze łatwiejszy sposób.

Ale na czym właściwie polega ta propozycja? Jej celem jest możliwość wykorzystania znaku ? jako jednego z parametrów danej funkcji, dzięki czemu automatycznie stawałaby się ona funkcją wyższego rzędu.

To co teraz możemy realizować poprzez bind...

const addOne = add.bind(null, 1);
addOne(2); // 3

...w przyszłości zrealizujemy poprzez:

const addOne = add(1, ?); // apply from the left
addOne(2); // 3

Zwróć uwagę na znak ? na liście parametrów funkcji add. W świecie który znamy dzisiaj w tym miejscu otrzymalibyśmy błąd składni. W świecie nowego ECMAScriptu będzie to poprawny kawałek kodu.

Zagrożeniem które widzę w przypadku takiej składni jest możliwe traktowanie znaku ? przez początkujących jako coś związanego z parametrami opcjonalnymi, więc jestem naprawdę ciekaw czy kiedyś taką zmianę zobaczymy w naszej przeglądarce.

do expressions

W jaki sposób przypisać coś do zmiennej na podstawie kilku różnych warunków?

Można to zrobić na przykład tak:

let calculatedValue = ''
if (firstCondition) {
    calculatedValue = basedOnFirstPath()
} else if (secondCondition) {
    calculatedValue = basedOnSecondPath()
} else {
    calculatedValue = basedOnThirdPath()
}

W epoce do expressions będziemy to jednak robić nieco inaczej:

let calculatedValue = do {
    if (firstCondition) { basedOnFirstPath() }
    else if (secondCondition) { basedOnSecondPath() }
    else { basedOnThirdPath() }
};

Prawda, że ma to sens? Jeśli ma, to trzymajcie kciuki za do!

🤗 Zobacz więcej propozycji zmian na etapie pierwszym

Luźne dyskusje - Stage 0

Stage 0 to najbardziej nieformalny etap z punktu widzenia danej zmiany. Wszystko co znajduje się na tym etapie może, ale wcale nie musi trafić do naszych przeglądarek. API każdej z propozycji może się też w znaczący sposób zmienić, a więc traktujcie te punkty z ogromnym dystansem.

Dekoratory parametrów

Dekoratory parametrów to zmiana podobna do tej którą opisywałem wyżej w kontekście metod, tym razem tyczy się jednak parametrów.

Co moglibyśmy umieścić w dekoratorze parametru? A chociażby taką walidację, którą znowu - jak w przypadku logiki "technicznej" - moglibyśmy wynieść poza ciało danej metody. Drugi przykład? Oznaczenia dla mechanizmu Dependency Injection, dzięki któremu określalibyśmy w jaki sposób wstrzykiwane mają być poszczególne zależności w klasie z którą pracujemy.

Zmiana ciekawa, ale na razie w sferze szkiców i luźnych dyskusji - jeszcze poczekamy.

nested imports

Aktualnie moduły ESM pozwalają na import poszczególnych zależności tylko na najwyższym poziomie danego modułu. Dlaczego chcielibyśmy to zmieniać?

Zobaczcie chociażby ten przykład:

describe('something', () => {

        it('should pass', () => {
            import { doCheck } from './service.js';
            strictEqual(doCheck(), 'works!');
        });

})

Założę się, że wiele z waszych testów lub komponentów zawiera dziesiątki importów na najwyższym poziomie modułu tylko z powodu tego ograniczenia związanego z zagnieżdżaniem importów (a właściwie z brakiem takiej możliwości).

Zmiana dość kontrowersyjna, bo niby dlaczego chcielibyśmy wspierać kolejne zagnieżdżenia? Po bliższym przyjrzeniu się tej propozycji okazuje się, że ma ona swoje mocne strony, takie jak chociażby powyższy przykład związany z testami. Czy jednak wejdzie ona do przeglądarek? Pozostaje na razie czekać.

🤭 Zobacz więcej propozycji zmian na etapie zero

TC39, czyli więcej transparencji i zwinności

Nowa forma procesu TC39 to większa transparencja oraz zwinność - zgodnie z ustaleniami standard ECMAScript ma teraz wzbogacać się o nowe funkcjonalności każdego roku, więc warto trzymać rękę na pulsie w konkteście tego co nadchodzi.

W dzisiejszym wpisie pokazałem wam osiem przykładowych zmian, jednak lista tego co nadchodzi jest o wiele większa! Wszystko nad czym obecnie toczy się dyskusja znajdziecie w repozytorium grupy roboczej TC39, a konkretnie w tym miejscu.

Jestem ogromnie ciekaw na jakie zmiany czekacie wy oraz co z tej listy powoduje u was największą ekscytację? Czy są to dekoratory? Top level await? A może Observable albo throw expressions? Dajcie znać w komentarzach!