typeof oznacza w TypeScript dwie rzeczy. W wyrażeniu to operator JavaScriptu: wykonuje się, zwraca string taki jak "string" albo "number", a TypeScript zawęża zmienną, gdy porównujesz ten string. W pozycji typu to zapytanie o typ: typeof config daje statyczny typ zmiennej config.
Ostatnia linia pokazuje różnicę: typeof custom w console.log to operator czasu wykonania i wypisuje object, a typeof defaults po type Options = to zapytanie w czasie kompilacji, które nigdy nie trafia do JavaScriptu.
Dwa znaczenia w skrócie
typeof w czasie wykonania (JavaScript) | Zapytanie o typ typeof (TypeScript) | |
|---|---|---|
| Gdzie się pojawia | W dowolnym wyrażeniu: if, return, console.log(...) | W pozycjach typu: po :, w type X = ..., wewnątrz <...> |
| Przykład | typeof x === "string" | let y: typeof x; |
| Obliczane | Gdy kod się wykonuje | Przez kompilator, a potem usuwane |
| Wynik | Jeden z ośmiu stringów | Typ |
| Do czego | Sprawdzanie wartości i zawężanie jej typu | Wyprowadzanie typu z wartości |
TypeScript rozróżnia je po pozycji, więc to samo słowo kluczowe nigdy nie oznacza obu rzeczy naraz.
Wyniki typeof w czasie wykonania
Operator czasu wykonania zwraca jeden z ośmiu stringów. Uruchom to, żeby zobaczyć, co dają typowe wartości:
| Wartość | Wynik typeof | Uwaga |
|---|---|---|
"hi", template stringi | "string" | |
42, 3.14, NaN, Infinity | "number" | NaN to liczba, sprawdzaj go przez Number.isNaN |
10n | "bigint" | |
true, false | "boolean" | |
undefined | "undefined" | |
Symbol("id") | "symbol" | |
| funkcje, funkcje strzałkowe, klasy | "function" | klasa to funkcja konstruktora |
null | "object" | historyczny błąd JavaScriptu |
{}, [], new Date(), new Map() | "object" | tablice to obiekty: użyj Array.isArray |
new String("x"), new Number(1) | "object" | obiekty opakowujące, unikaj ich |
W zwykłym JavaScripcie typeof na zmiennej, której nigdy nie zadeklarowano, zwraca "undefined" zamiast rzucać wyjątek. W TypeScript taki kod się nie kompiluje: Cannot find name 'notDeclared'. (TS2304).
Zawężanie przez typeof
Porównanie typeof w if, switch, operatorze trójargumentowym albo po && zawęża zmienną w każdej gałęzi. To standardowy sposób obsługi unii typów prostych i pierwsze sprawdzenie, po które warto sięgnąć przy unknown.
typeof x !== "string" zawęża w drugą stronę, do wszystkiego poza string. Kompilator zna osiem możliwych stringów, więc literówka w porównaniu to błąd kompilacji, a nie sprawdzenie, które po cichu zawsze daje false:
Kompilator wypisuje index.ts(3,7): error TS2367: This comparison appears to be unintentional because the types '"bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"' and '"strnig"' have no overlap. W zwykłym JavaScripcie ta literówka po cichu wysłałaby każdy string ścieżką dla number. Popraw pisownię, a kod wypisze text.
Pułapka typeof null
Ponieważ typeof null to "object", TypeScript zawęża typeof x === "object" do object | null, a nie do object. Dodaj sprawdzenie null, bo inaczej odczyt właściwości jest błędem (TS18047 'x' is possibly 'null'.).
typeof nie odróżni tablic, dat, map i zwykłych obiektów: wszystkie to "object". Dla tablic używaj Array.isArray(x), a dla instancji klas x instanceof Date. Ani typeof, ani nic innego nie sprawdzi w czasie wykonania, czy wartość pasuje do interfejsu, bo interfejsy są usuwane przy kompilacji. Do tego napisz funkcję type guard, która sprawdza właściwości.
typeof w pozycjach typu
W typie typeof someVariable kopiuje typ, który TypeScript wywnioskował (albo który zadeklarowano) dla tej zmiennej. Oszczędza ręcznego pisania typu dla wartości, która już istnieje, i utrzymuje oba w zgodzie.
const theme = {
primary: "#3178c6",
spacing: 8,
dark: false,
};
type Theme = typeof theme;
// { primary: string; spacing: number; dark: boolean }
function withSpacing(t: Theme, factor: number): Theme {
return { ...t, spacing: t.spacing * factor };
}
console.log(withSpacing(theme, 2)); // { primary: '#3178c6', spacing: 16, dark: false }
let userName = "Ada";
const fixed = "Ada";
type A = typeof userName; // string
type B = typeof fixed; // "Ada" (a const keeps its literal type)
Wynik zależy od tego, jak zadeklarowano zmienną: let albo właściwość obiektu są poszerzane (string, number), a typ prosty w const zachowuje typ literału. Dodaj as const do obiektu albo tablicy, a typeof da typy literałów readonly na każdym poziomie. typeof przyjmuje też dostęp do właściwości, typeof theme.spacing to number, ale nie dowolne wyrażenia: type T = typeof getTheme(); to błąd składni (';' expected., TS1005). Dla typu zwracanego przez wywołanie użyj ReturnType<typeof getTheme>.
keyof typeof: klucze obiektu jako typ
keyof typeof obj to najczęstsze połączenie. typeof zamienia obiekt w typ, keyof bierze jego klucze, a ty dostajesz unię nazw właściwości, która pozostaje poprawna, gdy obiekt się zmienia.
Ta sama sztuczka na stałej tablicy daje unię jej wartości: przy const roles = ["admin", "editor"] as const typ (typeof roles)[number] to "admin" | "editor". Bez as const byłby to tylko string. Sam keyof omawia strona o keyof.
ReturnType<typeof fn> i typeof na klasach
Typy narzędziowe, takie jak ReturnType i Parameters, przyjmują typ funkcji. Nazwa funkcji to wartość, więc najpierw przepuść ją przez typeof:
Zapis ReturnType<createUser> kończy się błędem 'createUser' refers to a value, but is being used as a type here. Did you mean 'typeof createUser'? (TS2749), czyli kompilator prosi dokładnie o to.
Klasy to jedyny przypadek, gdy nazwa jest jednocześnie wartością i typem. User jako typ oznacza instancję klasy, a typeof User oznacza samą klasę, czyli konstruktor razem z jego statycznymi składowymi:
class Point {
static origin = new Point(0, 0);
constructor(public x: number, public y: number) {}
}
const p: Point = new Point(1, 2); // an instance
const Ctor: typeof Point = Point; // the class (constructor + statics)
const q = new Ctor(3, 4); // q: Point
type Instance = InstanceType<typeof Point>; // Point
typeof, instanceof i type guard
| Sprawdzenie | Działa na | Przykład |
|---|---|---|
typeof | typy proste, funkcje, "czy to obiekt" | typeof x === "number" |
Array.isArray | tablice | Array.isArray(x) |
instanceof | instancje klas (Date, Error, twoje klasy) | x instanceof Date |
in | unie obiektów, według właściwości | "email" in x |
| Funkcja type guard | interfejsy, aliasy typów, cała reszta | isUser(x) z x is User |
Wybierz najwęższe narzędzie, które pasuje. Dla instancji klas zobacz instanceof. Dla kształtów opisanych interfejsem jedynym dostępnym sprawdzeniem w czasie wykonania jest funkcja type guard.
Najczęściej zadawane pytania
Co robi typeof w TypeScript?
Dwie różne rzeczy zależnie od miejsca. W wyrażeniu typeof x to operator JavaScriptu: wykonuje się i zwraca string, taki jak "string" albo "object", a TypeScript zawęża x, gdy porównujesz ten string. W adnotacji typu typeof x to zapytanie o typ: oblicza je kompilator, daje statyczny typ zmiennej x i znika z wyemitowanego JavaScriptu.
Czym jest keyof typeof w TypeScript?
keyof typeof obj daje unię nazw właściwości obiektu jako typy literałów string. typeof obj zamienia wartość obiektu w jej typ, a keyof bierze klucze tego typu. Dla const colors = { red: "#f00", blue: "#00f" } typ keyof typeof colors to "red" | "blue".
Dlaczego typeof null zwraca "object"?
To błąd z pierwszej wersji JavaScriptu, którego nie da się już naprawić bez psucia stron internetowych. TypeScript go odwzorowuje: po typeof x === "object" zmienna x jest zawężona do object | null, więc przed odczytem właściwości trzeba też sprawdzić x !== null.
Jak sprawdzić, czy wartość jest tablicą w TypeScript?
Użyj Array.isArray(value), co zawęża do typu tablicy. typeof tego nie potrafi: typeof [] to "object", tak samo jak dla zwykłych obiektów i null.
Czym różni się typeof od instanceof w TypeScript?
typeof sprawdza podstawową kategorię wartości ("string", "number", "function", "object"...). instanceof sprawdza, czy obiekt został utworzony przez konkretną klasę albo konstruktor, na przykład Date albo twoją class User. Używaj typeof dla typów prostych, a instanceof dla instancji klas. Żaden z nich nie sprawdzi interfejsu ani aliasu typu, bo te nie istnieją w czasie wykonania.