Przy powtarzaniu podobnych deklaracji w TypeScript szuka się obliczania wyniku z typu wejściowego. Ważniejsza od liczby znaków jest czytelność relacji między typami. Fikcyjna usługa powiadomień o przesyłkach przedstawia typy warunkowe i infer, oddzielając je od sprawdzania rzeczywistej poprawności danych zewnętrznych.
1. Typ warunkowy jest rozgałęzieniem przyjmującym typ
Oficjalna dokumentacja opisuje wybór wyniku przez extends. Przykład tworzy logiczny typ literałowy zależnie od string. Nie jest funkcją sprawdzającą napisy podczas działania.
type IsText<T> = T extends string ? true : false;
type NameIsText = IsText<'parcel'>; // true
type CountIsText = IsText<3>; // false
Załóżmy, że funkcja powiadomień przyjmuje identyfikator tekstowy, a wewnętrzna liczbowy. Przed typem warunkowym umiej wyjaśnić rozróżnienie. Inny sens uzasadnia osobne nazwy; sama notacja może być czytelniejsza po ujednoliceniu wejścia. Nie maskuj niejasnego projektu danych techniką typów.
Zapisuj oczekiwane wejścia i wyniki parami. Małe przykłady tekstu, liczb, unii i niespodziewanych wejść ułatwiają złożone deklaracje. Sprawdź zgodność komentarza i typu wnioskowanego przez edytor. Naturalna nazwa nie gwarantuje obliczonego wyniku.
2. Dystrybucja unii zmienia znaczenie tablicy
Gdy parametr typu stoi bezpośrednio po lewej warunku, warunek rozdziela się na członków unii. Aby traktować całość jako jednostkę, porównaj zapis w nawiasach kwadratowych.
type SeparateLists<T> = T extends unknown ? T[] : never;
type Together<T> = [T] extends [unknown] ? T[] : never;
type A = SeparateLists<string | number>; // string[] | number[]
type B = Together<string | number>; // (string | number)[]
const a: A = ['a', 'b'];
const b: B = ['a', 1];
Pierwszy oznacza tablicę samych tekstów lub samych liczb. Drugi dopuszcza je razem. Wybór zależy od tego, czy cała lista ma zawierać jeden rodzaj kodów, czy różne rodzaje w elementach. Nawiasy wyglądają podobnie, kontrakty nie.
Przy błędach wkładaj członków osobno zamiast czytać od razu cały złożony utility. Porównaj tekst, liczbę i ich unię. Gdy widać zamierzoną dystrybucję, można uzasadnić nawiasy. W zapisie kontroli zachowuj przykłady dozwolonych wartości, nie tylko wynik.
3. infer nazywa typ wewnątrz struktury
infer nadaje nazwę części struktury dopasowanej w warunku. Mały przykład zdejmuje jedną warstwę Promise.
type OneResult<T> = T extends Promise<infer Value> ? Value : T;
type Parcel = OneResult<Promise<{ tracking: string }>>;
type ParcelList = OneResult<Promise<string[]>>;
Kod nie uruchamia asynchroniczności ani nie czeka, tylko wyraża kształt przy kompilacji. Nazwa Resolved może sugerować otrzymaną wartość: sprawdź kontekst. Oczekiwanie podczas działania nadal obsługuje kod asynchroniczny.
Jeśli potrzeba rekurencyjnie oczekiwanego wyniku, sprawdź standardowy Awaited przed rozbudową przykładu. Prostota nauki i wymagania wspólnego narzędzia są różne. Dokumentuj dla zespołu wejścia o innym wyniku niż standard oraz uzasadnienie różnicy.
4. Łączenie nazw zdarzeń literałami szablonowymi
Typy literałów szablonowych wyrażają kombinacje napisów. Usługa może połączyć obszar i działanie w nazwę zdarzenia.
type Scope = 'parcel' | 'address';
type Action = 'created' | 'updated';
type EventName = `${Scope}:${Action}`;
const event: EventName = 'parcel:created';
Kompilacja wykrywa literówki, ale nie dowodzi ważności każdej kombinacji w usłudze. Gdy obsługiwane jest tylko usuwanie adresu, iloczyn obszaru i działania może źle opisywać regułę. Najpierw rozważ jawne przyporządkowanie dozwolonych zdarzeń i payloadów.
Przy wielu zdarzeniach czytelnik musi łatwo znaleźć rzeczywiste nazwy. Zamiast wielopoziomowych reguł łączenia można trzymać nazwy dla systemów zewnętrznych w pliku. Porównaj, jak nowy członek wykryje błędne zdarzenie przed i po abstrakcji. Wyrafinowanie i użyteczność oceniaj razem.
5. Zewnętrzny JSON sprawdza się podczas działania
Typ User lub Parcel przy odpowiedzi API nie gwarantuje rzeczywistej struktury. Przyjmij jako unknown, potem sprawdź wymagane pola.
function hasTracking(value: unknown): value is { tracking: string } {
return typeof value === 'object' && value !== null
&& 'tracking' in value && typeof value.tracking === 'string';
}
const incoming: unknown = { tracking: 'DEMO-001' };
if (hasTracking(incoming)) console.log(incoming.tracking);
Test potwierdza tylko tekst w tracking, nie istnienie numeru ani uprawnienie użytkownika do przesyłki. Zawężanie, reguły biznesowe i kontrola dostępu to różne odpowiedzialności. Dokumentacja Narrowing wyjaśnia zawężanie warunkami.
6. Mała kolejność wyboru narzędzi typów
Zapisz najpierw dwie realne wartości dopuszczone i dwie odrzucone. Wyraź sens zwykłą unią i jawnym obiektem. Gdy zostają powtarzane relacje, typ warunkowy i infer mają jasny powód. Jeżeli czytanie trwa znacznie dłużej, wydziel tylko wspólne części, resztę pozostaw zwykłą.
Oddzielnie wykonuj kontrolę typów i runtime. Odrzucenie złej nazwy przez kompilator i zrozumiały błąd przy brakujących polach serwera uzupełniają się. Po małych przykładach połącz kontrolę z rzeczywistą granicą wejścia, by przekształcenia pomagały bezpiecznie projektować aplikację.