2.11.20227 min

Jose Ramon Granja MartinezSenior Frontend Developer

5 bibliotek TypeScript dla Twojej bazy kodu

Poznaj 5 bibliotek, za pomocą których wykorzystasz w pełni potencjał TypeScript i wyeliminujesz najczęstsze błędy.

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: 'joe@no-reply.com'};

// ❌ 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: 'joe@no-reply.com'};

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.

trendy npm i dominacja type-fest w 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:

Jak przypisać komentarze do swoich metod w TypeScript


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..

<p>Loading...</p>