Menu

TypeScript typeof: Type Guards and the typeof Type Operator

typeof has two jobs in TypeScript. In code it is the JavaScript operator that returns "string", "number", "object" and so on at run time, and TypeScript narrows on it. In a type it is the type query that copies the type of a variable, as in keyof typeof obj and ReturnType<typeof fn>.

This page includes runnable editors - edit, run, and see output instantly.

typeof means two things in TypeScript. In an expression it is the JavaScript operator: it runs, returns a string like "string" or "number", and TypeScript narrows the variable when you compare that string. In a type position it is a type query: typeof config gives the static type of the variable config.

The last line shows the difference: typeof custom in a console.log is the runtime operator and prints object, while typeof defaults after type Options = is the compile-time query and never reaches the JavaScript.

The Two Meanings at a Glance

Runtime typeof (JavaScript)Type query typeof (TypeScript)
Where it appearsAny expression: if, return, console.log(...)Type positions: after :, in type X = ..., inside <...>
Exampletypeof x === "string"let y: typeof x;
EvaluatedWhen the code runsBy the compiler, then erased
ResultOne of eight stringsA type
Used forChecking a value and narrowing its typeDeriving a type from a value

TypeScript tells them apart by position, so the same keyword never means both at once.

typeof Results at Runtime

The runtime operator returns one of eight strings. Run this to see what common values give:

Valuetypeof resultNote
"hi", template strings"string"
42, 3.14, NaN, Infinity"number"NaN is a number; check it with Number.isNaN
10n"bigint"
true, false"boolean"
undefined"undefined"
Symbol("id")"symbol"
functions, arrow functions, classes"function"a class is a constructor function
null"object"a historical bug in JavaScript
{}, [], new Date(), new Map()"object"arrays are objects: use Array.isArray
new String("x"), new Number(1)"object"wrapper objects, avoid them

In plain JavaScript, typeof on a variable that was never declared returns "undefined" instead of throwing. In TypeScript that code does not compile: Cannot find name 'notDeclared'. (TS2304).

Narrowing With typeof

A typeof comparison in an if, a switch, a ternary or after && narrows the variable in each branch. It is the standard way to handle a union of primitives, and the first check to reach for on unknown.

typeof x !== "string" narrows the other way, to everything except string. And the compiler knows the eight possible strings, so a typo in the comparison is a compile error rather than a check that is silently always false:

The compiler prints 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. In plain JavaScript the typo would quietly send every string down the number path. Fix the spelling and it prints text.

The typeof null Trap

Because typeof null is "object", TypeScript narrows typeof x === "object" to object | null, not object. Add a null check, or reading a property is an error (TS18047 'x' is possibly 'null'.).

typeof cannot tell arrays, dates, maps and plain objects apart: all of them are "object". Use Array.isArray(x) for arrays and x instanceof Date for class instances. Neither typeof nor anything else can check whether a value matches an interface at run time, because interfaces are erased; for that, write a type guard function that checks the properties.

typeof in Type Positions

In a type, typeof someVariable copies the type TypeScript inferred (or you declared) for that variable. It saves writing a type by hand for a value that already exists, and keeps the two in sync.

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)

The result depends on how the variable was declared: a let or an object property is widened (string, number), a const primitive keeps its literal type. Add as const to an object or array and typeof gives readonly literal types all the way down. typeof also accepts property access, typeof theme.spacing is number, but not arbitrary expressions: type T = typeof getTheme(); is a syntax error (';' expected., TS1005). For the type a call returns, use ReturnType<typeof getTheme>.

keyof typeof: Keys of an Object as a Type

keyof typeof obj is the most common combination. typeof turns the object into a type, keyof takes its keys, and you get a union of the property names that stays correct as the object changes.

The same trick on a constant array gives a union of its values: with const roles = ["admin", "editor"] as const, the type (typeof roles)[number] is "admin" | "editor". Without as const it would only be string. The keyof page covers keyof on its own.

ReturnType<typeof fn> and typeof on Classes

Utility types such as ReturnType and Parameters take a function type. A function name is a value, so pass it through typeof first:

Writing ReturnType<createUser> fails with 'createUser' refers to a value, but is being used as a type here. Did you mean 'typeof createUser'? (TS2749), which is the compiler asking for exactly this.

Classes are the one case where a name is both a value and a type. User as a type means an instance of the class; typeof User means the class itself, the constructor, including its static members:

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 a Type Guard

CheckWorks onExample
typeofprimitives, functions, "is it an object"typeof x === "number"
Array.isArrayarraysArray.isArray(x)
instanceofclass instances (Date, Error, your classes)x instanceof Date
inobject unions, by property"email" in x
Type guard functioninterfaces, type aliases, anything elseisUser(x) with x is User

Pick the narrowest tool that fits. For class instances see instanceof; for shapes described by an interface, a type guard function is the only runtime check available.

Frequently Asked Questions

What does typeof do in TypeScript?

Two different things depending on where it appears. In an expression, typeof x is the JavaScript operator: it runs and returns a string such as "string" or "object", and TypeScript narrows x when you compare that string. In a type annotation, typeof x is a type query: it is evaluated by the compiler and gives the static type of the variable x, and it disappears from the emitted JavaScript.

What is keyof typeof in TypeScript?

keyof typeof obj gives the union of an object's property names as string literal types. typeof obj turns the object value into its type, and keyof takes the keys of that type. For const colors = { red: "#f00", blue: "#00f" }, keyof typeof colors is "red" | "blue".

Why does typeof null return "object"?

It is a bug from the first version of JavaScript that can no longer be fixed without breaking the web. TypeScript models it: after typeof x === "object", x is narrowed to object | null, so you must also check x !== null before reading properties.

How do I check if a value is an array in TypeScript?

Use Array.isArray(value), which narrows to an array type. typeof cannot do it: typeof [] is "object", the same as for plain objects and null.

What is the difference between typeof and instanceof in TypeScript?

typeof checks the primitive category of a value ("string", "number", "function", "object"...). instanceof checks whether an object was created by a particular class or constructor, such as Date or your own class User. Use typeof for primitives and instanceof for class instances; neither can check an interface or type alias, which do not exist at run time.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED