Każda karta należy do jednej zasady, a każda zasada ma karty po obu stronach — pary, które przechodzą, i te, które nie. Kwadraty są twoje: karta ostatnio trafiona, ostatnio chybiona i jeszcze niespotkana.
Typ literałowy to jedna wartość swojego typu prostego, więc go rozszerza — nigdy odwrotnie i nigdy w poprzek typów prostych: "42" to string, a nie number. Literał liczbowy to jego wartość, jakkolwiek zapisana: 0x10 to 16.
"a" extends string
truestring extends "a"
TS2322
Unia po prawej to wybór: co rozszerza jeden z jej członów, rozszerza całą unię. PropertyKey to string | number | symbol.
"a" extends "a" | "b"
true"c" extends "a" | "b"
TS2322
Unia po lewej musi pasować w całości: każdy jej człon musi rozszerzać prawą stronę. boolean to po prostu true | false.
"a" | "b" extends string
true"a" | "b" extends "a"
TS2322
Przy strictNullChecks null i undefined to osobne typy. Pasują tylko tam, gdzie je wymieniono — albo do unknown i any, a samo undefined także do void.
Każdy typ rozszerza unknown. Samo unknown rozszerza tylko to, co dopuszcza każdą wartość: unknown, any — albo {} | null | undefined, czyli ten sam zbiór rozpisany na części.
string extends unknown
trueunknown extends string
TS2322
never to typ bez żadnych wartości, więc rozszerza wszystko — a jego samego nie rozszerza nic poza never.
never extends string
truestring extends never
TS2322
07any wyłącza sprawdzanie
Gdziekolwiek stoi any, ta część porównania przechodzi, w obie strony. Nie zmienia jednak struktury wokół siebie: brakująca właściwość albo zła długość krotki nadal nie przechodzą.
{} znaczy „wszystko poza null i undefined”, więc typy proste je rozszerzają; object je wyklucza. A jednak {} rozszerza object, choć string rozszerza {} — przypisywalność nie jest przechodnia.
{ a: string } extends {}
truenull extends {}
TS2322
Nazwy wielką literą to interfejsy obiektów opakowujących. Typ prosty rozszerza swoje opakowanie i każdy typ obiektowy zbudowany z jego składowych, tak jak opakowanie je typuje — length napisu to number, nawet dla "abc". Opakowanie nigdy nie rozszerza typu prostego, a Object zachowuje się jak {}.
string extends String
trueString extends string
TS2322
11Właściwość po właściwości
Właściwości porównuje się po jednej, w tym samym kierunku co całość: węższy typ właściwości pasuje, szerszy nie.
12Opcjonalne to nie | undefined
Właściwości opcjonalnej może w ogóle nie być; a: T | undefined musi istnieć. Dlatego wymagana pasuje do opcjonalnej, a opcjonalna do wymaganej już nie.
Typ, którego wszystkie właściwości są opcjonalne, jest „słaby”. Wszystko, co ma właściwości, ale nie dzieli z nim żadnej, zostaje odrzucone z TS2559 — choć strukturalnie by pasowało.
14Właściwości readonly się nie liczą
readonly przy właściwości ogranicza to, co wolno zrobić przez ten typ, a przypisywalność je ignoruje: { readonly a: string } i { a: string } rozszerzają się nawzajem.
Tablica węższych elementów rozszerza tablicę szerszych. Przy zapisie to dziura — wstaw liczbę do string[] widzianego jako unknown[] — a TypeScript i tak na to pozwala.
16Tablice readonly zostają readonly
Tablica lub krotka readonly nie ma metod modyfikujących, więc nie wypełni zmiennego miejsca (TS4104). Zwykła tablica do miejsca readonly pasuje bez zastrzeżeń.
Krotka to tablica, której długość jest częścią typu — [1, 2] ma length: 2. Krotki rozszerzają tablice swoich elementów; tablica nigdy nie rozszerza krotki, bo może mieć dowolną długość.
[string, number] extends (string | number)[]
true(string | number)[] extends [string, number]
TS2322
18Parametry działają odwrotnie
Przy strictFunctionTypes funkcja rozszerza inną tylko wtedy, gdy przyjmuje wszystko, co tamta może dostać: typy parametrów porównuje się odwrotnie, a parametr opcjonalny może dostać undefined. Przyjmować więcej to bezpieczny kierunek.
19Mniej parametrów jest w porządku
JavaScript pozwala funkcji ignorować argumenty, więc ta z mniejszą liczbą parametrów rozszerza tę z większą. Nie przechodzi dopiero wymaganie dodatkowego argumentu; parametr resztowy liczy się jako dowolnie wiele.
Typy zwracane porównuje się w tym samym kierunku co funkcje: funkcja zwracająca coś węższego rozszerza funkcję zwracającą coś szerszego.
21void znaczy „zignorowane”
Typ funkcji zwracającej void obiecuje tylko, że nikt nie przeczyta wyniku, więc rozszerza go funkcja zwracająca cokolwiek. Samo void nie jest ani undefined, ani null: przyjmują je tylko void, unknown i any.
Składnia metody wyłącza strictFunctionTypes: parametry metody porównuje się w obu kierunkach, a rozstrzyga składnia po stronie docelowej. Właściwość o typie funkcyjnym dostaje ścisłą kontrolę. Dlatego Set<"a"> rozszerza Set<string>.
23Function, generyki i konstruktory
Każda funkcja rozszerza Function, ale Function nie obiecuje żadnej konkretnej sygnatury. Funkcja generyczna rozszerza każdą instancję, na którą pozwala jej ograniczenie, nie odwrotnie — a konstruktor abstrakcyjny nie wypełni miejsca, które wymaga new.
Typ z kilkoma sygnaturami wywołania rozszerza pojedynczą sygnaturę, jeśli pasuje do niej którekolwiek z przeciążeń. W drugą stronę jedna sygnatura musi pasować do każdego przeciążenia celu, po kolei.
Sygnatura indeksu wymaga, by pasowała do niej każda właściwość. Literał typu obiektowego dostaje ją niejawnie; interfejs nie, object też nie — dlatego żaden z nich nie rozszerza Record<string, unknown>, a ten sam kształt jako alias typu tak. Wyjątkiem jest sygnatura typu any: przyjmuje każdy obiekt.
26Typy szablonów literałowych
Typ szablonu literałowego to wzorzec na napisy. ${string} pasuje do każdego tekstu, także pustego; ${number} — do każdego niepustego napisu, który JavaScript odczyta jako skończoną liczbę: "1e3" i "0x1F" tak, "Infinity" nie; ${bigint} — tylko do całkowitych. Lowercase<string> i pokrewne pasują do tekstu już zapisanego w tej wielkości liter.
Przecięcie ma właściwości wszystkich członów, więc rozszerza każdy z nich. Takie, którego nie spełni żadna wartość — string & number, dwa różne literały kind — redukuje się do never, a never rozszerza wszystko.
Obiekt rozszerza unię obiektów, gdy pasuje do jednego z członów. Od TypeScriptu 3.5 obiekt, którego dyskryminant sam jest unią, rozbija się na osobny obiekt dla każdej wartości i każdy kawałek jest sprawdzany — dlatego { kind: "a" | "b" } rozszerza { kind: "a" } | { kind: "b" }.
keyof to unia znanych kluczy typu: klucze liczbowe pozostają liczbami, klucze tablicy obejmują nazwę każdej metody, a klucze sygnatury indeksu typu string to string | number. keyof {} i keyof unknown to never; keyof never to każdy klucz, jaki istnieje.
T[K] to typ właściwości K w T, a unia kluczy daje unię typów. Na tablicy T[number] to typ elementu; na krotce — unia jej elementów, a T["length"] — jej dosłowna długość. Właściwość opcjonalna przynosi ze sobą undefined.
31Typy pomocnicze to zwykłe typy
Partial, Required, Pick, Omit i Record rozwijają się do zwykłych typów obiektowych i są sprawdzane dokładnie tak jak one — właściwość po właściwości.
32Typy wyliczane rozwijają się najpierw
ReturnType, Parameters, Awaited, Exclude, Extract i NonNullable to typy warunkowe. Kompilator rozwija każdy z nich do zwykłego typu, zanim cokolwiek porówna — dlatego ReturnType<() => void> to void, a NonNullable<unknown> to {}.
33Klasy są strukturalne — do czasu private
Dwie klasy o tym samym publicznym kształcie rozszerzają się nawzajem, a zwykły typ obiektowy może rozszerzać klasę; składowe statyczne należą do konstruktora, więc się nie liczą. Składowa private, protected albo #private czyni klasę nominalną: tę składową ma tylko jej własna deklaracja i jej podklasy.
Enum liczbowy wciąż przyjmuje dowolny number i każdy literał będący jedną z jego wartości — ale nie literał spoza zakresu. Każda składowa rozszerza własną wartość, więc S.A rozszerza "a"; a jednak enum napisowy przyjmuje tylko własne składowe i "a" to nie S.A.
enum E { A, B }E.A extends number
trueenum E { A, B }E extends E.A
TS2322
unique symbol to literał typu symbol: rozszerza symbol i PropertyKey, symbol nie rozszerza jego, a żadne dwa takie symbole nie rozszerzają się nawzajem. Dobrze znane symbole, jak Symbol.iterator, też są unikalne.
Typ generyczny porównuje się przez jego argument, w kierunku, w jakim typ go używa: właściwości i wyniki kowariantnie — także właściwości zmienne — a parametry typów funkcyjnych kontrawariantnie. in i out podają kierunek wprost, ponad tym, co zmierzyłby kompilator; in out czyni go niezmienniczym.