typeof bedeutet in TypeScript zwei Dinge. In einem Ausdruck ist es der JavaScript-Operator: Er läuft, gibt einen String wie "string" oder "number" zurück, und TypeScript engt die Variable ein, wenn du diesen String vergleichst. An einer Typposition ist es eine Type Query: typeof config liefert den statischen Typ der Variablen config.
Die letzte Zeile zeigt den Unterschied: typeof custom in einem console.log ist der Laufzeitoperator und gibt object aus, während typeof defaults nach type Options = die Abfrage beim Kompilieren ist und nie im JavaScript ankommt.
Die zwei Bedeutungen im Überblick
typeof zur Laufzeit (JavaScript) | Type Query typeof (TypeScript) | |
|---|---|---|
| Wo es steht | In jedem Ausdruck: if, return, console.log(...) | An Typpositionen: nach :, in type X = ..., in <...> |
| Beispiel | typeof x === "string" | let y: typeof x; |
| Ausgewertet | Wenn der Code läuft | Vom Compiler, danach entfernt |
| Ergebnis | Einer von acht Strings | Ein Typ |
| Verwendet für | Einen Wert prüfen und seinen Typ einengen | Einen Typ aus einem Wert ableiten |
TypeScript unterscheidet sie anhand der Position, dasselbe Schlüsselwort bedeutet also nie beides zugleich.
Ergebnisse von typeof zur Laufzeit
Der Laufzeitoperator gibt einen von acht Strings zurück. Führe das aus, um zu sehen, was gängige Werte ergeben:
| Wert | Ergebnis von typeof | Hinweis |
|---|---|---|
"hi", Template-Strings | "string" | |
42, 3.14, NaN, Infinity | "number" | NaN ist eine Zahl; prüfe es mit Number.isNaN |
10n | "bigint" | |
true, false | "boolean" | |
undefined | "undefined" | |
Symbol("id") | "symbol" | |
| Funktionen, Arrow Functions, Klassen | "function" | eine Klasse ist eine Konstruktorfunktion |
null | "object" | ein historischer Bug in JavaScript |
{}, [], new Date(), new Map() | "object" | Arrays sind Objekte: Array.isArray verwenden |
new String("x"), new Number(1) | "object" | Wrapper-Objekte, meide sie |
In reinem JavaScript gibt typeof auf eine nie deklarierte Variable "undefined" zurück, statt zu werfen. In TypeScript kompiliert dieser Code nicht: Cannot find name 'notDeclared'. (TS2304).
Narrowing mit typeof
Ein Vergleich mit typeof in einem if, einem switch, einem ternären Ausdruck oder nach && engt die Variable in jedem Zweig ein. Das ist der Standardweg, eine Union aus primitiven Typen zu behandeln, und die erste Prüfung, zu der du bei unknown greifst.
typeof x !== "string" engt in die andere Richtung ein, auf alles außer string. Und der Compiler kennt die acht möglichen Strings, ein Tippfehler im Vergleich ist also ein Compilerfehler statt einer Prüfung, die stillschweigend immer false ist:
Der Compiler gibt 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. aus. In reinem JavaScript würde der Tippfehler still jeden String in den Pfad für number schicken. Korrigiere die Schreibweise, dann gibt es text aus.
Die Falle typeof null
Weil typeof null gleich "object" ist, engt TypeScript typeof x === "object" auf object | null ein, nicht auf object. Ergänze eine Prüfung auf null, sonst ist das Lesen einer Eigenschaft ein Fehler (TS18047 'x' is possibly 'null'.).
typeof kann Arrays, Datumswerte, Maps und einfache Objekte nicht unterscheiden: Alle sind "object". Nimm Array.isArray(x) für Arrays und x instanceof Date für Klasseninstanzen. Weder typeof noch sonst etwas kann zur Laufzeit prüfen, ob ein Wert zu einem Interface passt, weil Interfaces entfernt werden; dafür schreibst du eine Type-Guard-Funktion, die die Eigenschaften prüft.
typeof an Typpositionen
In einem Typ kopiert typeof someVariable den Typ, den TypeScript für diese Variable abgeleitet hat (oder den du deklariert hast). Das erspart es, einen Typ für einen bereits vorhandenen Wert von Hand zu schreiben, und hält beide synchron.
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)
Das Ergebnis hängt davon ab, wie die Variable deklariert wurde: Eine let oder eine Objekteigenschaft wird erweitert (string, number), ein primitiver Wert in einer const behält seinen Literaltyp. Ergänze as const an einem Objekt oder Array, dann liefert typeof readonly Literaltypen bis ganz nach unten. typeof akzeptiert auch Eigenschaftszugriffe, typeof theme.spacing ist number, aber keine beliebigen Ausdrücke: type T = typeof getTheme(); ist ein Syntaxfehler (';' expected., TS1005). Für den Typ, den ein Aufruf zurückgibt, nimm ReturnType<typeof getTheme>.
keyof typeof: die Schlüssel eines Objekts als Typ
keyof typeof obj ist die häufigste Kombination. typeof macht das Objekt zu einem Typ, keyof nimmt dessen Schlüssel, und du bekommst eine Union der Eigenschaftsnamen, die korrekt bleibt, wenn sich das Objekt ändert.
Derselbe Trick auf einem konstanten Array liefert eine Union seiner Werte: Mit const roles = ["admin", "editor"] as const ist der Typ (typeof roles)[number] gleich "admin" | "editor". Ohne as const wäre er nur string. Die Seite zu keyof behandelt keyof für sich.
ReturnType<typeof fn> und typeof bei Klassen
Utility Types wie ReturnType und Parameters nehmen einen Funktionstyp. Ein Funktionsname ist ein Wert, also führst du ihn zuerst durch typeof:
ReturnType<createUser> zu schreiben scheitert mit 'createUser' refers to a value, but is being used as a type here. Did you mean 'typeof createUser'? (TS2749), und genau darum bittet der Compiler hier.
Klassen sind der eine Fall, in dem ein Name zugleich Wert und Typ ist. User als Typ bedeutet eine Instanz der Klasse; typeof User bedeutet die Klasse selbst, den Konstruktor, einschließlich seiner statischen Member:
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 vs instanceof vs ein Type Guard
| Prüfung | Funktioniert bei | Beispiel |
|---|---|---|
typeof | primitiven Werten, Funktionen, „ist es ein Objekt“ | typeof x === "number" |
Array.isArray | Arrays | Array.isArray(x) |
instanceof | Klasseninstanzen (Date, Error, deine Klassen) | x instanceof Date |
in | Unions aus Objekten, anhand einer Eigenschaft | "email" in x |
| Type-Guard-Funktion | Interfaces, Typaliasse, alles andere | isUser(x) mit x is User |
Nimm das engste Werkzeug, das passt. Für Klasseninstanzen siehe instanceof; für Formen, die ein Interface beschreibt, ist eine Type-Guard-Funktion die einzige verfügbare Prüfung zur Laufzeit.
Häufig gestellte Fragen
Was macht typeof in TypeScript?
Je nach Stelle zwei verschiedene Dinge. In einem Ausdruck ist typeof x der JavaScript-Operator: Er läuft und gibt einen String wie "string" oder "object" zurück, und TypeScript engt x ein, wenn du diesen String vergleichst. In einer Typannotation ist typeof x eine Type Query: Der Compiler wertet sie aus, sie liefert den statischen Typ der Variablen x und verschwindet aus dem erzeugten JavaScript.
Was ist keyof typeof in TypeScript?
keyof typeof obj liefert die Union der Eigenschaftsnamen eines Objekts als String-Literaltypen. typeof obj macht aus dem Objektwert seinen Typ, und keyof nimmt die Schlüssel dieses Typs. Für const colors = { red: "#f00", blue: "#00f" } ist keyof typeof colors gleich "red" | "blue".
Warum gibt typeof null "object" zurück?
Das ist ein Bug aus der ersten Version von JavaScript, der sich nicht mehr beheben lässt, ohne das Web kaputt zu machen. TypeScript bildet ihn ab: Nach typeof x === "object" ist x auf object | null eingeengt, du musst also zusätzlich x !== null prüfen, bevor du Eigenschaften liest.
Wie prüfe ich in TypeScript, ob ein Wert ein Array ist?
Mit Array.isArray(value), das auf einen Array-Typ einengt. typeof kann das nicht: typeof [] ist "object", genau wie bei einfachen Objekten und null.
Was ist der Unterschied zwischen typeof und instanceof in TypeScript?
typeof prüft die primitive Kategorie eines Werts ("string", "number", "function", "object"...). instanceof prüft, ob ein Objekt von einer bestimmten Klasse oder einem Konstruktor erzeugt wurde, etwa Date oder deine eigene class User. Nimm typeof für primitive Werte und instanceof für Klasseninstanzen; keins von beiden kann ein Interface oder einen Typalias prüfen, denn die existieren zur Laufzeit nicht.