5 bibliotek TypeScript dla Twojej bazy kodu

TypeScript w ostatnich latach to nic innego jak rozwój. Jest to ulubiony język wielu web developerów i coraz rzadziej już pracuje się ze zwykłą bazą kodu JavaScriptu.
Czasami jednak możliwości TypeScriptu nie są wykorzystywane w pełni. Zbyt duża ilość konwersji lub stosowanie any należą do najczęściej popełnianych błędów.
W tym artykule poznamy listę 5 bibliotek, które wzbogacą Twoje doświadczenie z TypeScriptem i zwiększą Twoje zaufanie. Te minimalistyczne biblioteki zwiększą ergonomię pracy każdego dewelopera.
1. zod
Słabym punktem TypeScriptu jest to, że jest zatwierdzany tylko w czasie kompilacji. Po parsowaniu i budowie, wszystkie typy są usuwane. Może to prowadzić do niektórych niepożądanych błędów, gdy np.:
- kompilator ufa deweloperowi, przyjmując pewne założenia (użycie
any
,ts-expect-error
, konwersje, itd...) - sieć zwraca inny schemat REST niż oczekiwany.
Zobaczmy przykład tego ostatniego:
interface User {
name: string;
email: string;
}
async function fetchUser(id: string): User {
const url = `/users/${id}`;
const response = await fetch(url);
return (await response.json()) as User;
}
W powyższym kodzie kompilator ufa, że sieć będzie zwracała obiekt JSON User
z właściwościami name
i email
. Jeśli okaże się to nieprawdą, czeka nas sporo problemów w produkcji. I niestety, możemy to zauważyć dopiero w runtime’ie.
Dzięki zod
możemy zdefiniować schemat, który będzie również zatwierdzany podczas runtime’u.
Zobaczmy refaktoryzację poprzedniego kodu z użyciem zod
const UserSchema = z.object({
name: z.string(),
email: z.string(),
});
async function fetchUser(id: string) {
const url = `/users/${id}`;
const response = await fetch(url);
// ✅ will throw an error if not matching which can be logged in sentry
return UserSchema.parse(await response.json());
}
Możemy wybrać sposób postępowania z błędami. W powyższym przykładzie UserSchema.parse
wyrzuci błąd w runtime’ie.
Mogliśmy również zdecydować, aby nie wyrzucać błędu, używając metody safeParse
. Jest to idealne rozwiązanie do rejestrowania problemów w programie Sentry bez zakłócania user experience.
Zod jest dość przydatny, możemy również wyodrębnić typ ze schematu za pomocą z.infer
i taki schemat możemy propagować w całym kodzie.
const UserSchema = z.object({
name: z.string(),
email: z.string(),
});
export type User = z.infer<typeof UserSchema>;
Podsumowując, jest to niezawodna biblioteka, dzięki której upewnisz się, że żaden z twoich typów nie jest nieprawidłowy, co mogłoby prowadzić do wielu niespójności w kodzie.
Inne ważne kwestie zod:
- Jest malutka: 8kb zminifikowane + skompresowane
- Zero zależności
- Niezmienna
- Spójny, łańcuchowy interfejs
- Podejście funkcjonalne
- Działa również ze zwykłym JavaScriptem! Nie musisz używać TypeScriptu.
Wymagania zod
- TypeScript 4.1 lub wyższy
- Włączony tryb ścisły
Instalacja zod
npm install --save-dev zod
2. tiny Invariant
Zdarzają się pewne miejsca w kodzie, w których jesteś pewien, że nie jest to wartość nullable. W trybie ścisłym TypeScript będzie to błąd, jeśli nie umieścisz sprawdzania null. Można to obejść z pomocą natywnego operatora !
.
Rozważmy taki przykład:
interface User {
name?: string;
email?: string;
}
const u: User = { name: 'Joe', email: '[email protected]'};
// ❌ Error: Object is possibly 'undefined'
console.log(u.name.toUpperCase());
// ✅ Compiles
console.log(u.name!.toUpperCase());
Powyższe jest złą praktyką, ponieważ czasami te założenia mogą być błędne.
TypeScript ma do tego celu przeznaczoną natywną funkcję zwaną Asssertion Functions
. Biblioteka tiny-invariant
jest na niej zbudowana.
Możesz nakarmić tiny-invariant
założeniem i wiadomością do rzucenia, jeśli jest falsy
. Będzie wyrzucać wyjątki w runtime’ie, gdy tylko te założenia nie zostaną spełnione. Możesz to później wyłapać na Sentry lub w innym oprogramowaniu. Dzięki temu będziesz miał większe zaufanie do twojej bazy kodu i wykryjesz wszelkie niespójności.
Zobaczmy taki przykład:
import invariant from 'tiny-invariant';
interface User {
name?: string;
email?: string;
}
const u: User = { name: 'Joe', email: '[email protected]'};
invariant(u.name, 'Name should not be null for this scenario')
// ✅ Compiles without the need of `!`
console.log(u.name.toUpperCase());
Baza kodu staje się bardziej odporna i spójna. Możemy teraz bezpiecznie wyczyścić wiele z tych zbędnych instrukcji if
.
Ten pakiet jest minimalny, możesz zdecydować się na wdrożenie własnej funkcji invariant
, jeśli tylko tak chcesz to zrobić.
Wymagania tiny Invariant
- Włączony tryb ścisły (w przeciwnym razie jest to bezcelowe)
Instalacja tiny Invariant
npm install --save-dev tiny-invariant
3. type-fest
Jedną z najważniejszych funkcji TypeScriptu są typy mapowane. TypeScript posiada kilka narzędzi, jednak są one ograniczone i mają być tylko punktem wyjścia.
W rezultacie, najprawdopodobniej, posiadasz kolekcję narzędzi w swojej bazie kodu. Prawdopodobnie w utils.d.ts
, co możesz przenosić z projektu do projektu. I jest to całkowicie w porządku, jednak istnieją inne sposoby rozwiązania problemu.
Jest kilka narzędzi bibliotecznych Type, które można wykorzystać. Zwróć uwagę, że niektóre są już na wymarciu, jak np. ts-toolbet
. Zapewniają przetestowane w boju typy, które zmniejszają czas, jaki poświęcasz na pisanie nowych mapowań. Patrząc na trendy npm można zauważyć, jak bardzo type-fest
dominuje w tej przestrzeni.
Spójrzmy na przykład. Natywne narzędzie Optional
TypeScript jest dość ograniczone. Pozwala nam jedynie oznaczyć wszystkie właściwości jako opcjonalne. Brakuje w nim szczegółowości.
Sprawdźmy, z czym przychodzi do nas type-fest
w odniesieniu do tego przypadku użycia:
import {SetOptional, OptionalKeysOf } from 'type-fest';
interface User {
name: string;
email: string;
}
// Native Utility: all keys are marked as partial
type PartialUser = Partial<User>;
// Result:
// {
// name?: string
// email?: string;
// }
// 💪 Only selected keys are optional
type PartialUserEmail = SetOptional<User, 'email'>;
// Result:
// {
// name: string
// email?: string | undefined;
// }
// 💪 Fetching optional keys from type
type PartialKeys = OptionalKeysOf<PartialUserEmail>;
// Result:
// email
Zauważcie, jak silnego i jak niewielkiego kodu jednocześnie potrzebujemy, aby to zrobić. A ponieważ wszystkie typy są usuwane w czasie kompilacji, nie zwiększy to rozmiaru twojej paczki.
Pamiętajcie jednak, że wszystkie te narzędzia biblioteczne mają pewne wymagania i ograniczenia. Możliwe więc, że będziecie musieli aktualizować TypeScript w nowej wersji lub umieścić kod w strict mode
.
Wymagania dla type-fest
- TypeScript 4.7 lub wyższy
- tryb ścisły
Instalacja type-fest
npm install --save-dev type-fest
4. ts-morph
Istnieje mnóstwo różnych scenariuszy, w których przydatne jest wykonywanie statycznej analizy na kodzie. Możesz tutaj zastosować jscodeshift
lub nawet babel
. Jednak warto mieć tutaj więcej poglądu na temat typowań.
W tym celu musisz pozostać przy użyciu kompilatora TypeScript, co może przełożyć się na stromą krzywą uczenia się. Na szczęście projekt ts-morph
już dawno został rozpoczęty. Zapewnia łatwiejszy sposób programowego nawigowania i manipulowania kodem TypeScript.
W jaki sposób ts-morph to osiąga?
- Poprzez zapewnienie pewnych wrapperów wokół API kompilatora.
- Umożliwienie powrotu do API kompilatora
- Wykonywanie wszystkich zmian najpierw w pamięci i wysyłanie do kodu tylko wtedy, gdy jest to wskazane
Spójrzmy na prosty przykład, w którym sprawdzimy, czy istnieje Enum, a jeśli tak, to dokonamy tutaj zmiany. Instalacja jest dość prosta
npm install --save-dev ts-morph
Musimy stworzyć plik example.ts
, aby uruchomić nasz kod.
import { Project } from 'ts-morph';
const project = new Project();
const OLD_FUNCTION_NAME = 'addingNumbers';
const NEW_FUNCTION_NAME = 'sum'
// ✅ scoping changes to a location
project.addSourceFilesAtPaths('src/**/*.ts');
project.getSourceFiles().forEach((sourceFile) => {
// ✅ fetching targets to inspect
const functions = sourceFile.getFunctions();
// ✅ filtering to desired target
functions.forEach((item) => {
if (item.getName() === OLD_FUNCTION_NAME) {
// ✅ rename function and its usages
item.rename(NEW_FUNCTION_NAME);
}
});
// ✅ emitting to the file
sourceFile.save();
});
A następnie musimy go tylko uruchomić:
npx ts-node example.ts
5. Type-docs
Proces tworzenia dokumentacji jest kluczowym aspektem podczas budowania API. Pomaga innym programistom szybko zorientować się, co eksponuje twoja aplikacja, a każdy język ma zazwyczaj swój własny proces budowania dokumentacji.
W przypadku TypeScript nie ma tutaj wbudowanych narzędzi i dlatego narodził się TypeDoc
. Używał komentarzy do kodu, aby zbudować dokumentację w HTML lub JSON. Jest on rozszerzalny i obsługuje różne konfiguracje.
Narzędzie to posiada poręczną dokumentację, którą można znaleźć pod adresem https://typedoc.org. Znajdują się tam łatwe przykłady do prześledzenia, dzięki którym można opanować to narzędzie.
Spójrzmy na przykład, gdzie zobaczymy, w jaki sposób można przypisać komentarze do swoich metod:
/**
* Calculates the square root of a number.
*
* @param x the number to calculate the root of.
* @returns the square root if `x` is non-negative or `NaN` if `x` is negative.
*/
export function sqrt(x: number): number {
return Math.sqrt(x);
}
Poniżej znajdziesz wyniki:
Można tu bardzo dużo uzyskać:
- czysty interfejs
- breadcrumbs
- nawigacja boczna
- łatwe do dostosowania CSS pozwala na dostosowanie go do własnych potrzeb
Instalacja Type-docs
npm install typedoc --save-dev
Aby wygenerować dokumentację, musi wiedzieć o pliku konfiguracyjnym tsconfig.json
. Odliczy go na podstawie podanego do programu punktu wejścia.
typedoc src/index.ts
Można również zdefiniować wiele punktów wejścia:
typedoc src/package1/index.ts src/package2/index.ts
Zamiast przekazywać plik, możesz przekazać folder, a TypeDoc użyje opcji entryPointStrategy
, która będzie szukać pliku index
.
Strategia ta ułatwia generowanie dokumentacji dla obszarów roboczych poprzez uruchomienie.
// ✅ it will check in each package folder for an index.ts file
typedoc --entryPointStrategy packages .
Zakończenie
I to by było na tyle, każda z tych pięciu bibliotek TypeScript to kandydat do zwiększenia ergonomii dowolnego z twoich projektów. Jak wspomniałem już wcześniej, niektóre bardzo łatwo wprowadzisz do swojej pracy. Wszystko sprowadza się tylko do Twoich możliwości rozwojowych i priorytetów.
Z oryginałem artykułu w języku angielskim można zapoznać się na blogu better programming..