TypeScript – dlaczego warto go znać?
Wstęp
Tworzenie dużych aplikacji webowych napisanych JavaScriptem to nie taka prosta rzecz. Nie mamy do dyspozycji typów, interfejsów, enumów itp. Te funkcje mogłyby być bardzo pomocne przy serwisowaniu naszych skomplikowanych aplikacji. Aby uporać się z tym problemem, możemy wykorzystać TypeScript.
Zgodnie z opisem widocznym na stronie głównej TypeScript, jest to JavaScript, który się skaluje. Jest to typowany nadzbiór JavaScriptu, który kompiluje się do zwykłego JavaScriptu. Oznacza to, że każdy kod JavaScript to również prawidłowy kod TypeScript.
Autor zakłada, że czytelnicy mają podstawową wiedzę w zakresie JavaScriptu. Wszystkie przykłady opisane w tym artykule można przetestować w Typescript Playground.
Typy
Możemy typować wszystkie zmienne, nie tylko w formie typów prymitywnych (ciągi znaków, wartości logiczne, liczby), ale także jako typy lub nawet interfejsy. Pozwala nam to na pozyskanie informacji o wszystkich dostępnych metodach lub właściwościach danego typu, np. w przypadku „number” widzimy metody ‘toFixed’ lub ‘toExponential’. Jeśli spróbujemy metody niedostępnej w danym prototypie, pokaże się nam błąd kompilacji.
const num: number = 12;
const str: string = 'string';
str.toFixed(2); // Property 'toFixed' does not exist on type 'string'
Wyobraźmy sobie taką sytuację: Używamy REST Api, by konsumować dane z serwera. Fajnie by było wyświetlić wszystkie właściwości dostępne dla tej odpowiedzi w naszym IDE.
Możemy stworzyć typ, który obejmuje wszystkie właściwości pobrane z serwera.
type ApiResponse = {
name: string;
surname: string;
car: {
name: string;
doors: number;
}
};
fetch(’/api’)
.then((res: Response) => res.json())
.then((res: ApiResponse) => {
res.name; // ok
res.age; // Property 'age' does not exist on type 'ApiResponse'
})
Oczywiście wymaga to aktualizacji typu w chwili, gdy zajdą zmiany w api, ale jeśli użyjemy TypeScript, to zarówno po stronie frontendowej, jak i backendowej, możemy wykorzystać go do typowania odpowiedzi. Nie ma potrzeby korzystać z dwóch różnych typów.
Interfejsy
Interfejsy są podobne do typów. W tym przypadku możemy jednak wykorzystać je do wdrożenia metod i właściwości interfejsów w klasach.
interface InterfaceToImplement {
str: string;
doSth: () => Promise<any>
};
class MyClass implements InterfaceToImplement {
str: string = ’some string’;
doSth() {
return Promise.resolve();
}
}
Enumy z ciągami znaków
Od wersji 2.4 TypeScript możliwe jest korzystanie z enumów z ciągami znaków. Może się to przydać do ujednolicania nazw akcji lub tworzenia mapy kolorów. Można też wykorzystać je jako typ.
enum Color {
Gray = ’#95a5a6’,
Green = ’#2ecc71’,
Blue = ’#3498db’
}
const myColor: Color = Color.Green; // ok
const chosenColor: Color = ’#2ecc71’; // Type '"#2ecc71"' is not assignable to type 'Color'.
Async/Await
Te konstrukcje są przydatne, gdy chcemy użyć funkcji async i zapisać je w bardziej zsynchronizowany sposób. Domyślnie transpilują się do ES6. Jeśli chcemy transpilować je do ES5 lub nawet ES3, musimy dodać polyfill dla funkcji Promise do tsconfig.json.
async function greetMe(name: string): Promise<string> {
const greeting = await Promise.resolve(`Hi ${name}`);
return greeting;
}
greetMe(’John’).then(greeting => console.log(greeting)); // Hi John
Keyof
Ta funkcja dostępna jest od wersji TypeSript 2.1. Pozwala na deklaracje zestawu wartości, z którego utworzone zostaną inne obiekty.
interface UserConfiguration {
name: string;
age: number;
}
type UserConfigurationOption = keyof UserConfiguration;
function keyValue<T extends UserConfigurationOption>(key: T, value: UserConfiguration[T]) {
console.log(`Setting: ${key} to ${value}`);
}
keyValue('name', 'John');
keyValue('age', 25);
keyValue('age', true); // Argument of type 'true' is not assignable to parameter of type 'number'.
Podsumowanie
Powyższe to zaledwie krótki przegląd niektórych funkcji dostępnych w TypeScript. Pokazałem dziś najbardziej użyteczne funkcje TypeScriptu, które nie są dostępne w ES6.