Co nowego w TypeScript 5.0: deklaratory, typy stałe, ulepszenia wyliczeń, szybkość i wiele więcej!
Opublikowany: 2023-04-15TypeScript 5.0 został oficjalnie wydany 16 marca 2023 roku i jest teraz dostępny dla wszystkich. W tej wersji wprowadzono wiele nowych funkcji, których celem jest zmniejszenie, uproszczenie i przyspieszenie języka TypeScript.
Ta nowa wersja unowocześnia dekoratory do dostosowywania klas, umożliwiając dostosowywanie klas i ich członków w sposób wielokrotnego użytku. Deweloperzy mogą teraz dodawać modyfikator const do deklaracji parametru typu, umożliwiając wnioskowanie podobne do const jako domyślne. Nowa wersja sprawia również, że wszystkie wyliczenia są sumami, upraszczając strukturę kodu i przyspieszając działanie TypeScript.
W tym artykule poznasz zmiany wprowadzone w TypeScript 5.0, zapewniając dogłębne spojrzenie na jego nowe funkcje i możliwości.
Pierwsze kroki z TypeScript 5.0
TypeScript to oficjalny kompilator, który możesz zainstalować w swoim projekcie za pomocą npm. Jeśli chcesz zacząć używać TypeScript 5.0 w swoim projekcie, możesz uruchomić następujące polecenie w katalogu swojego projektu:
npm install -D typescript
Spowoduje to zainstalowanie kompilatora w katalogu node_modules , który można teraz uruchomić za pomocą polecenia npx tsc
.
W tej dokumentacji można również znaleźć instrukcje dotyczące korzystania z nowszej wersji języka TypeScript w programie Visual Studio Code.
Co nowego w TypeScript 5.0?
W tym artykule przyjrzyjmy się 5 głównym aktualizacjom wprowadzonym do TypeScript. Funkcje te obejmują:
Zmodernizowani dekoratorzy
Dekoratory są już od jakiegoś czasu w TypeScript pod flagą eksperymentalną, ale nowa wersja przyspiesza ich wraz z propozycją ECMAScript, która jest teraz na etapie 3, co oznacza, że jest na etapie, w którym zostanie dodana do TypeScript.
Dekoratory to sposób na dostosowanie zachowania klas i ich członków w sposób wielokrotnego użytku. Na przykład, jeśli masz klasę, która ma dwie metody, greet
i getAge
:
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
W rzeczywistych przypadkach ta klasa powinna mieć bardziej skomplikowane metody, które obsługują pewną logikę asynchroniczną i mają skutki uboczne itp., w przypadku których chciałbyś wrzucić kilka wywołań console.log
, aby pomóc w debugowaniu metod.
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log('LOG: Method Execution Starts.'); console.log(`Hello, my name is ${this.name}.`); console.log('LOG: Method Execution Ends.'); } getAge() { console.log('LOG: Method Execution Starts.'); console.log(`I am ${this.age} years old.`); console.log('Method Execution Ends.'); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
Jest to często występujący wzorzec i wygodnie byłoby mieć rozwiązanie, które można zastosować do każdej metody.
Tutaj do gry wkraczają dekoratorzy. Możemy zdefiniować funkcję o nazwie debugMethod
, która wygląda następująco:
function debugMethod(originalMethod: any, context: any) { function replacementMethod(this: any, ...args: any[]) { console.log('Method Execution Starts.'); const result = originalMethod.call(this, ...args); console.log('Method Execution Ends.'); return result; } return replacementMethod; }
W powyższym kodzie metoda debugMethod
przyjmuje oryginalną metodę ( originalMethod
) i zwraca funkcję, która wykonuje następujące czynności:
- Rejestruje komunikat „Rozpoczyna się wykonywanie metody”.
- Przekazuje oryginalną metodę i wszystkie jej argumenty (w tym this).
- Rejestruje komunikat „Koniec wykonywania metody”.
- Zwraca wszystko, co zwróciła oryginalna metoda.
Korzystając z dekoratorów, możesz zastosować debugMethod
do swoich metod, jak pokazano w poniższym kodzie:
class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } @debugMethod greet() { console.log(`Hello, my name is ${this.name}.`); } @debugMethod getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();
Spowoduje to wyświetlenie następujących informacji:
LOG: Entering method. Hello, my name is Ron. LOG: Exiting method. LOG: Entering method. I am 30 years old. LOG: Exiting method.
Podczas definiowania funkcji dekoratora ( debugMethod
) przekazywany jest drugi parametr o nazwie context
(jest to obiekt context — zawiera przydatne informacje o tym, jak została zadeklarowana dekorowana metoda, a także nazwę metody). Możesz zaktualizować debugMethod
, aby uzyskać nazwę metody z obiektu context
:
function debugMethod( originalMethod: any, context: ClassMethodDecoratorContext ) { const methodName = String(context.name); function replacementMethod(this: any, ...args: any[]) { console.log(`'${methodName}' Execution Starts.`); const result = originalMethod.call(this, ...args); console.log(`'${methodName}' Execution Ends.`); return result; } return replacementMethod; }
Po uruchomieniu kodu dane wyjściowe będą teraz zawierały nazwy każdej metody ozdobionej dekoratorem debugMethod
:
'greet' Execution Starts. Hello, my name is Ron. 'greet' Execution Ends. 'getAge' Execution Starts. I am 30 years old. 'getAge' Execution Ends.
Z dekoratorami można zrobić więcej. Zachęcamy do sprawdzenia oryginalnego żądania ściągnięcia, aby uzyskać więcej informacji na temat używania dekoratorów w TypeScript.
Przedstawiamy parametry typu const
To kolejne duże wydanie, które daje nowe narzędzie z typami ogólnymi w celu poprawy wnioskowania uzyskiwanego podczas wywoływania funkcji. Domyślnie, kiedy deklarujesz wartości za pomocą const
, TypeScript określa typ, a nie jego wartości literalne:
// Inferred type: string[] const names = ['John', 'Jake', 'Jack'];
Do tej pory, aby uzyskać pożądane wnioskowanie, trzeba było używać asercji const, dodając „as const”:
// Inferred type: readonly ["John", "Jake", "Jack"] const names = ['John', 'Jake', 'Jack'] as const;
Kiedy wywołujesz funkcje, jest podobnie. W poniższym kodzie wywnioskowany typ krajów to string[]
:
type HasCountries = { countries: readonly string[] }; function getCountriesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: string[] const countries = getCountriesExactly({ countries: ['USA', 'Canada', 'India'] });
Możesz potrzebować bardziej szczegółowego typu, którego jednym ze sposobów naprawy było dodanie asercji as const
:
// Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] } as const);
Może to być trudne do zapamiętania i wdrożenia. Jednak TypeScript 5.0 wprowadza nową funkcję, w której można dodać modyfikator const do deklaracji parametru typu, który automatycznie zastosuje jako domyślne wnioskowanie podobne do const.
type HasCountries = { countries: readonly string[] }; function getNamesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] });
Używanie parametrów typu const
umożliwia programistom wyraźniejsze wyrażanie intencji w ich kodzie. Jeśli zmienna ma być stała i nigdy się nie zmieniać, użycie parametru typu const
gwarantuje, że nigdy nie zostanie przypadkowo zmieniona.
Możesz sprawdzić oryginalne żądanie ściągnięcia, aby uzyskać więcej informacji na temat działania parametru typu const w TypeScript.
Ulepszenia wyliczeń
Wyliczenia w TypeScript to potężna konstrukcja, która umożliwia programistom definiowanie zestawu nazwanych stałych. W TypeScript 5.0 wprowadzono ulepszenia wyliczeń, aby uczynić je jeszcze bardziej elastycznymi i użytecznymi.
Na przykład, jeśli masz następujące wyliczenie przekazane do funkcji:
enum Color { Red, Green, Blue, } function getColorName(colorLevel: Color) { return colorLevel; } console.log(getColorName(1));
Przed wprowadzeniem TypeScript 5.0 można było podać zły numer poziomu i nie powodowało to żadnego błędu. Ale wraz z wprowadzeniem TypeScript 5.0 natychmiast zgłosi błąd.
Ponadto nowa wersja przekształca wszystkie wyliczenia w wyliczenia związkowe, tworząc unikalny typ dla każdego obliczonego elementu członkowskiego. To udoskonalenie pozwala na zawężanie wszystkich wyliczeń i odwoływanie się do ich członków jako typów:
enum Color { Red, Purple, Orange, Green, Blue, Black, White, } type PrimaryColor = Color.Red | Color.Green | Color.Blue; function isPrimaryColor(c: Color): c is PrimaryColor { return c === Color.Red || c === Color.Green || c === Color.Blue; } console.log(isPrimaryColor(Color.White)); // Outputs: false console.log(isPrimaryColor(Color.Red)); // Outputs: true
Ulepszenia wydajności TypeScript 5.0
TypeScript 5.0 zawiera wiele znaczących zmian w strukturze kodu, strukturach danych i rozszerzeniach algorytmicznych. Pomogło to udoskonalić całe środowisko TypeScript, od instalacji po wykonanie, czyniąc go szybszym i wydajniejszym.
Na przykład różnica między rozmiarem pakietu TypeScript 5.0 i 4.9 jest dość imponująca.
TypeScript został niedawno przeniesiony z przestrzeni nazw do modułów, co pozwala na wykorzystanie nowoczesnych narzędzi do budowania, które mogą przeprowadzać optymalizacje, takie jak podnoszenie zakresu. Ponadto usunięcie niektórych przestarzałych kodów zmniejszyło około 26,4 MB z 63,8 MB rozmiaru pakietu TypeScript 4.9.
Oto kilka bardziej interesujących zwycięstw pod względem szybkości i rozmiaru między TypeScript 5.0 a 4.9:
Scenariusz | Czas lub rozmiar w stosunku do TS 4.9 |
czas kompilacji interfejsu użytkownika | 90% |
Czas uruchamiania kompilatora TypeScript | 89% |
Czas budowy dramatopisarza | 88% |
Czas samodzielnego budowania kompilatora TypeScript | 87% |
Czas kompilacji programu Outlook Web | 82% |
Czas kompilacji VS Code | 80% |
maszynopis npm Rozmiar paczki | 59% |
Rozdzielczość pakietu dla lepszej rozdzielczości modułu
Kiedy piszesz instrukcję importu w TypeScript, kompilator musi wiedzieć, do czego odnosi się import. Robi to za pomocą rozdzielczości modułu. Na przykład, kiedy piszesz import { a } from "moduleA"
, kompilator musi znać definicję a
w moduleA
, aby sprawdzić jego użycie.
W TypeScript 4.7 dodano dwie nowe opcje dla ustawień --module
i moduleResolution
: node16
i nodenext
.
Celem tych opcji było dokładniejsze przedstawienie dokładnych reguł wyszukiwania dla modułów ECMAScript w Node.js. Jednak ten tryb ma kilka ograniczeń, które nie są wymuszane przez inne narzędzia.
Na przykład w module ECMAScript w Node.js każdy import względny musi zawierać rozszerzenie pliku, aby działał poprawnie:
import * as utils from "./utils"; // Wrong import * as utils from "./utils.mjs"; // Correct
TypeScript wprowadził nową strategię o nazwie „moduleResolution bundler”. Tę strategię można zaimplementować, dodając następujący kod w sekcji „compilerOptions” pliku konfiguracyjnego TypeScript:
{ "compilerOptions": { "target": "esnext", "moduleResolution": "bundler" } }
Ta nowa strategia jest odpowiednia dla osób korzystających z nowoczesnych programów pakujących, takich jak Vite, esbuild, swc, Webpack, Parcel i innych, które wykorzystują hybrydową strategię wyszukiwania.
Możesz sprawdzić oryginalne żądanie ściągnięcia i jego implementację, aby uzyskać więcej informacji na temat działania modułu moduleResolution
w języku TypeScript.
Wycofania
TypeScript 5.0 ma pewną amortyzację, w tym wymagania dotyczące środowiska wykonawczego, zmiany lib.d.ts i zmiany powodujące uszkodzenie interfejsu API.
- Wymagania dotyczące środowiska wykonawczego: TypeScript jest teraz ukierunkowany na ECMAScript 2018, a pakiet określa minimalne oczekiwania silnika na 12.20. Dlatego użytkownicy Node.js powinni mieć minimalną wersję 12.20 lub nowszą, aby używać TypeScript 5.0.
- lib.d.ts Zmiany: Wprowadzono pewne zmiany w sposobie generowania typów DOM, które mogą mieć wpływ na istniejący kod. W szczególności niektóre właściwości zostały przekonwertowane z typów liczbowych na literały liczbowe, a właściwości i metody obsługi zdarzeń wycinania, kopiowania i wklejania zostały przeniesione między interfejsami.
- Zmiany powodujące uszkodzenie API: Usunięto niektóre niepotrzebne interfejsy i wprowadzono pewne ulepszenia poprawności. TypeScript 5.0 również przeniósł się do modułów.
W TypeScript 5.0 wycofano niektóre ustawienia i odpowiadające im wartości, w tym target: ES3
, out
, noImplicitUseStrict
, keyofStringsOnly
, suppressExcessPropertyErrors
, suppressImplicitAnyIndexErrors
, noStrictGenericChecks
, charset
, importsNotUsedAsValues
i preserveValueImports
, a także poprzedzać w odniesieniach do projektów.
Chociaż te konfiguracje pozostaną ważne do wersji TypeScript 5.5, zostanie wydane ostrzeżenie, aby ostrzec użytkowników, którzy nadal z nich korzystają.
Streszczenie
W tym artykule poznałeś niektóre z głównych funkcji i ulepszeń, które wprowadza TypeScript 5.0, takie jak ulepszenia wyliczeń, rozdzielczości programu pakującego i parametrów typu const, a także ulepszenia szybkości i rozmiaru.
Jeśli myślisz o TypeScript do swoich następnych projektów, wypróbuj bezpłatnie Hosting aplikacji Kinsta.
Teraz twoja kolej! Jakie funkcje lub ulepszenia w TypeScript 5.0 uważasz za najbardziej atrakcyjne? Czy są jakieś istotne, które mogliśmy przeoczyć? Daj nam znać w komentarzach.