Przejdź do treści
webtype.orgwebtype.org

    ↑↓ ruch · ⏎ otwórz · esc zamknij

    Graj

    Extends

    Typ po lewej, typ po prawej, jedno naciśnięcie klawisza: czy rozszerza? Kart w talii: 586. Kompilator odpowiedział już na każdą z nich.

    Kart w talii: 586

    Którą gałąź wybierze kompilator?

    Każda karta to A extends B ? true : false. Odpowiedz, zanim dopali się lont. Pięć trafień z rzędu podnosi mnożnik; pomyłka kosztuje jedno z trzech żyć i pokazuje dokładnie, co powiedział kompilator.

    Dziś to samo rozdanie dla wszystkich. Liczy się pierwsza rozgrywka.

    T←trueF→falseEscpauza⏎start

    Ranga
    @ts-nocheck
    jeszcze 10 do @ts-check
    Seria dzienna
    0
    Znane karty
    0/586

    Wyniki, rangi i serie są zapisane w tej przeglądarce i nigdzie indziej.

    01Zasady gry

    Jedno pytanie, na każdej karcie inaczej

    1. 01

      Przeczytaj parę

      Dwa typy i słowo między nimi. Niektóre karty mają najpierw deklarację do przeczytania — interfejs, enum, klasę.

    2. 02

      Wybierz gałąź

      true albo false: T lub ←, F lub →, stuknięcie albo przesunięcie karty w stronę, o którą ci chodzi.

    3. 03

      Zdąż przed lontem

      Każda karta się dopala. Dobra odpowiedź daje 100 za poziom i do połowy tego za szybkość. Pomyłka albo koniec czasu kosztuje życie.

    4. 04

      Utrzymaj serię

      Co pięć trafień z rzędu mnożnik rośnie, aż do ×8. Piętnaście z rzędu zwraca stracone życie.

    02Talia

    Zasady przypisywalności — wszystkie 36

    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.

    01

    Literały i typy proste

    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
      true
    • string extends "a"
      TS2322
    Twój wynik: 0 z 30

    Lekcja: Typy warunkowe

    02

    Do unii

    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
    Twój wynik: 0 z 22

    Referencja: Extract

    03

    Z unii

    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
    04

    null i undefined

    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.

    • undefined extends unknown
      true
    • null extends string
      TS2322
    05

    Szczyt: unknown

    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
      true
    • unknown extends string
      TS2322
    Twój wynik: 0 z 16

    Błąd: TS18046

    06

    Dno: never

    never to typ bez żadnych wartości, więc rozszerza wszystko — a jego samego nie rozszerza nic poza never.

    • never extends string
      true
    • string extends never
      TS2322
    07

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

    • string extends any
      true
    • Record<string, any> extends { a: number }
      TS2741
    Twój wynik: 0 z 15

    tsconfig: noImplicitAny

    08

    {} to nie pusty obiekt

    {} 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 {}
      true
    • null extends {}
      TS2322
    Twój wynik: 0 z 20
    09

    String to nie string

    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
      true
    • String extends string
      TS2322
    Twój wynik: 0 z 17
    10

    Nadmiarowe właściwości nie przeszkadzają

    Typy obiektowe są otwarte. Typ z większą liczbą właściwości rozszerza typ z mniejszą — Error to tylko interfejs z nazwą i komunikatem. Nie przechodzi dopiero brak wymaganej właściwości.

    • { a: string; b: number } extends { a: string }
      true
    • { a: string } extends { a: string; b: number }
      TS2741
    Twój wynik: 0 z 22

    Błąd: TS2741Błąd: TS2739

    11

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

    • { a: "x" } extends { a: string }
      true
    • { a: string } extends { a: "x" }
      TS2322
    Twój wynik: 0 z 16

    Błąd: TS2322

    12

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

    • { a: string } extends { a?: string }
      true
    • { a?: string } extends { a: string }
      TS2322
    13

    Kontrola słabych typów

    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.

    • { a: string; b: number } extends { a?: string }
      true
    • { b: number } extends { a?: string }
      TS2559
    14

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

    • { a: string } extends { readonly a: string }
      true
    • { readonly a: number } extends { a: string }
      TS2322
    15

    Tablice są kowariantne

    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.

    • string[] extends unknown[]
      true
    • string[] extends "a"[]
      TS2322
    Twój wynik: 0 z 16
    16

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

    • string[] extends readonly string[]
      true
    • readonly string[] extends string[]
      TS4104
    17

    Krotki mają długość

    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
    18

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

    • (x: string) => void extends (x: "a") => void
      true
    • (x: "a") => void extends (x: string) => void
      TS2322
    19

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

    • () => void extends (x: string) => void
      true
    • (x: string) => void extends () => void
      TS2322
    20

    Wyniki działają wprost

    Typy zwracane porównuje się w tym samym kierunku co funkcje: funkcja zwracająca coś węższego rozszerza funkcję zwracającą coś szerszego.

    • () => "a" extends () => string
      true
    • () => string extends () => "a"
      TS2322
    Twój wynik: 0 z 18

    Referencja: ReturnType

    21

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

    • () => number extends () => void
      true
    • () => void extends () => number
      TS2322
    Twój wynik: 0 z 13

    Błąd: TS2355

    22

    Metody są biwariantne

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

    • { m(x: string): void } extends { m(x: "a"): void }
      true
    • { m(x: number): void } extends { m(x: string): void }
      TS2322
    23

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

    • () => void extends Function
      true
    • Function extends () => void
      TS2322
    24

    Przeciążenia

    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.

    • { (x: string): string; (x: number): number } extends (x: string) => string
      true
    • (x: string) => string extends { (x: string): string; (x: number): number }
      TS2322
    Twój wynik: 0 z 5

    Błąd: TS2769

    25

    Sygnatury indeksu

    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.

    • { a: "x" } extends { [k: string]: string }
      true
    • { a: string; b: number } extends Record<string, string>
      TS2322
    26

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

    • "id-42" extends `id-${number}`
      true
    • "id-x" extends `id-${number}`
      TS2322
    27

    Przecięcia

    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.

    • { a: string } & { b: number } extends { a: string }
      true
    • { a: string } extends { a: string } & { b: number }
      TS2322
    28

    Unie obiektów

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

    • { kind: "a"; a: 1 } extends { kind: "a"; a: number } | { kind: "b" }
      true
    • { kind: "c" } extends { kind: "a" } | { kind: "b" }
      TS2322
    29

    keyof

    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.

    • keyof { a: 1; b: 2 } extends "a" | "b"
      true
    • "a" | "b" | "c" extends keyof { a: 1; b: 2 }
      TS2322
    30

    Dostęp indeksowany

    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.

    • { a: string }["a"] extends string
      true
    • { a: string }["a"] extends number
      TS2322
    32

    Typy 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 {}.

    • ReturnType<() => string> extends string
      true
    • ReturnType<() => string> extends number
      TS2322
    33

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

    • class Dog { name = "" } class Cat { name = "" }Cat extends Dog
      true
    • class A { x = 1 } class B extends A { y = 2 }A extends B
      TS2741
    Twój wynik: 0 z 12

    Wzorzec: Ostempluj prymityw

    34

    Enumy

    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
      true
    • enum E { A, B }E extends E.A
      TS2322
    Twój wynik: 0 z 15

    tsconfig: erasableSyntaxOnly

    35

    unique symbol

    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.

    • typeof Symbol.iterator extends symbol
      true
    • symbol extends typeof Symbol.iterator
      TS2322
    Twój wynik: 0 z 6

    Referencja: typeof

    36

    Generyki i wariancja

    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.

    • Promise<"a"> extends Promise<string>
      true
    • Promise<string> extends Promise<"a">
      TS2322

    03Sprawdzone

    Każdą kartę rozdał kompilator

    Każda karta jest kompilowana podczas budowania przez TypeScript 6.0.3 w trybie ścisłym, dwa razy: jako typ warunkowy, który pokazuje — musi się rozwinąć dokładnie do werdyktu z karty — i jako przypisanie, które musi się nie udać, gdy werdykt to false, i przejść, gdy to true. Build staje, jeśli którakolwiek karta się nie zgadza. Komunikat po pomyłce jest cytatem z tego przebiegu, nigdy nie jest pisany ręcznie.

    Karty
    586
    Rozszerzają
    340
    Nie rozszerzają — każda z diagnostyką
    246
    Kody diagnostyk
    6
    TS2322 · TS2559 · TS2739 · TS2740 · TS2741 · TS4104

    Wyniki, rangi i serie są zapisane w tej przeglądarce i nigdzie indziej.